動態規劃演算法通常基於乙個遞推公式及乙個或多個初始狀態。 當前子問題的解將由上一次子問題的解推出。使用動態規劃來解題只需要多項式時間複雜度, 因此它比回溯法、暴力法等要快許多。動態規劃也是面試筆試題中的乙個考查重點,當閱讀乙個題目並且開始嘗試解決它時,首先看一下它的限制。 如果要求在多項式時間內解決,那麼該問題就很可能要用dp來解。遇到這種情況, 最重要的就是找到問題的「狀態」和「狀態轉移方程」。(狀態不是隨便定義的, 一般定義完狀態,你要找到當前狀態是如何從前面的狀態得到的, 即找到狀態轉移方程)如果看起來是個dp問題,但你卻無法定義出狀態, 那麼試著將問題規約到乙個已知的dp問題。
這裡先說明乙個最簡單的動態規劃例項:硬幣問題。後續還會給出更多的例項,例如:最長公共子串行,最長公共子串,最長遞增子串行,字串編輯距離等。動態規劃的關鍵就是找出「狀態」和「狀態轉移方程」。
硬幣問題:給你一些面額的硬幣,然後給你乙個值n,要你求出構成n所需要的最少硬幣的數量和方案。分析:這個問題可以嘗試用貪心演算法去解決,先從面額最大的硬幣開始嘗試,一直往下找,知道硬幣總和為n。但是貪心演算法不能保證能夠找出解(例如,給,2,3,5,然後n=11)。我們可以換個思路,我們用d(i)表示求總和為i的最少硬幣數量(其實就是動態規劃中的「狀態」),那麼怎麼從前面的狀態(並不一定是d(i-1)這乙個狀態)到d(i)這個狀態?假設硬幣集合為coins[0~n],在求d(i)之前,我們假設d(1~i-1)全部都求出來了,那麼d(i)=min,if i-j 在coins中(其實這就是「狀態轉移方程」)。另 我們把每種面值看作乙個點!表示「還需要湊足的面值」,初始狀態為s,目標狀態為0。那麼若當前狀態在i,每使用乙個硬幣j,狀態便轉移到i-vj。
舉例說明:coins=,n=11。
d(0)=0;
d(1)=0;
d(2)=d(0)+1=1;
d(3)=d(0)+1=1;
d(4)=d(2)+1=2;
d(5)=min=1;
d(6)=min=2;
.......................
同時為了求出最後的方案(不僅僅是硬幣個數),需要記錄求每個狀態選擇的「路徑」,例如:求d(5)我們選擇了d(0)+1,那麼我們選擇的路徑就是5-0=5。我們必須記錄這些路徑,然後根據路徑得出結果。對於d(6),我們開始選擇了3,也就是說我們選擇了從d(3)狀態和硬幣3跳轉到d(6),接著對於d(3),我們選擇了3,也就是說我們選擇了從d(0)狀態和硬幣3跳轉到了d(3),接著對於d(0),這個是初始狀態。所以我們得方案是3,3。
遞推(列印最小序):
#include #include using namespace std;
const int maxn = 10000;
const int inf = 1000000000;
int n, s, v[maxn], minn[maxn], maxn[maxn];
//minn[i]表示還需湊足價值為i的話,所需的最少的硬幣數目!maxn[i]表示還需湊足價值為i的話,所需的最多的硬幣數目!
void print_ans(int* d, int s)
}}int main()
for(int i=1; i<=s; ++i)
for(int i=1; i<=s; ++i)
printf("minn=%d\n",minn[i]);
for(int i = 1; i <= s; ++i)
}} cout << minn[s] << endl;
cout << maxn[s] << endl;
print_ans(minn, s);
cout << endl;
print_ans(maxn, s);
cout << endl;
return 0;
}
另一種列印方式:
#include #include using namespace std;
const int maxn = 10000;
const int inf = 1000000000;
int n, s, v[maxn], minn[maxn], maxn[maxn];
//minn[i]表示還需湊足價值為i的話,所需的最少的硬幣數目!maxn[i]表示還需湊足價值為i的話,所需的最多的硬幣數目!
int min_coin[maxn], max_coin[maxn];
//min_coin[s]記錄的是滿足minn[s] = minn[s-v[i]]+1的最小的i。
void print_ans(int* d, int s)
}int main()
for(int i=1; i<=s; ++i)
for(int i = 1; i <= s; ++i)
if(maxn[i] < maxn[i - v[j]] + 1) }}
} cout << minn[s] << endl;
cout << maxn[s] << endl;
print_ans(min_coin, s);
cout << endl;
print_ans(max_coin, s);
cout << endl;
return 0;
}
硬幣問題(DP)
題目 有n種硬幣,面值分別為v1,v2,vn,每種都有無限多。給定非負整數s,可以選用多少個硬幣,使得面值之和恰好為s?輸出硬幣數目的最小值!狀態 d i 表示i 元最少要換多少個硬幣,那麼我們便要求d s 的值 狀態如何轉移 i元換零錢v j i v j 後狀態變為 d i v j 狀態選取 選d...
dp 最少硬幣問題
問題描述 設有n 種不同面值的硬幣,各硬幣的面值存於陣列t 1 n 中。現要用這些面值的硬幣 來找錢。可以使用的各種面值的硬幣個數存於陣列coins 1 n 中。對任意錢數0 m 20001,設計乙個用最少硬幣找錢m的方法。程式設計任務 對於給定的1 n 10,硬幣面值陣列t和可以使用的各種面值的硬...
最少硬幣問題 dp思想
最少硬幣問題 description 設有n種不同面值的硬幣,各硬幣的面值存於陣列t 1 n 中。現要用這些面值的硬幣來找錢。可以使用的各種面值的硬幣個數存於陣列coins 1 n 中。對任意錢數0 m 20001,設計乙個用最少硬幣找錢m的方法。對於給定的1 n 10,硬幣面值陣列t和可以使用的各...