1. 程式人生 > 實用技巧 >動態規劃法

動態規劃法

動態規劃演算法和分治法類似,其基本思想也是將待求解問題分解成若干子問題,但是經分解得到的子問題的數目只是多項式量級,而且子問題不是互相獨立的。可以避免分治法中子問題被重複計算很多次。

兩個要素:(1)子問題重疊性質(2)最優子結構性質

子問題重疊性質:(遞迴演算法求解時)有些子問題被反覆計算多次。動態規劃法中對每一個子問題只解一次。

最優子結構性質:當一個問題的最優解包含了其子問題的最優解。

全源最短路徑

Floyed演算法採用自底向上方式計算,dist[i][j]存放從結點i到j的最短路徑的長度,在演算法的第k步上,做出決策:從i到j的最短路徑上是否包含結點k,時間複雜度O(n^3)

最長公共子序列

dp[i][j] 表示第一個序列的前i個字元和第二個序列的前j個字元的最長公共子序列的長度,動態轉移方程為:

dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + (A[i]==B[j] ? 1 : 0)),表示在這三種狀態中取到最大值,

(1)第一種狀態表示不錄入第一個序列的第i個字元時的最長公共子序列; (2)第二種狀態表示不錄入第二個序列的第j個字元時的最長公共子序列; (3)第三種狀態表示第一個序列的前i-1個字元與第二個序列前j-1個字元的公共子序列加上最後一個字元的錄入狀態,如果最後的一個字元相等則錄入1,否則為0。 然後根據動歸的狀態,來判斷我們要求得的序列中的字元有哪些。
#define INF 0x3f3f3f3f
char a[1001], b[1001];
int dp[1001][1001], len1, len2;
void lcs(int i, int j)
{
    for(i=1; i<=len1; i++)
    {
        for(j=1; j<=len2; j++)
        {
            if(a[i-1] == b[j-1])///a[i]和b[j]相等
                dp[i][j] = dp[i-1][j-1] + 1;
            else if(dp[i-1][j] > dp[i][j-1
]) dp[i][j] = dp[i-1][j]; else dp[i][j] = dp[i][j-1]; } } } void llcs()///根據dp求得最長公共子序列 { int i, j, z = 0; char c[1001]; memset(c, 0, sizeof(c)); i = len1, j = len2; while(i!=0 && j!=0) { if(a[i-1] == b[j-1]) { c[z++] = a[--i]; j--; } else if(dp[i-1][j] < dp[i][j-1]) j--; else if(dp[i][j-1] <= dp[i-1][j]) i--; } for(i=z-1; i>=0; i--) printf("%c", c[i]); printf("\n"); } int main() { while(scanf(" %s", a)!=EOF) { scanf(" %s", b); memset(dp, 0, sizeof(dp)); len1 = strlen(a); len2 = strlen(b); lcs(len1, len2); llcs(); } return 0; }
最長公共子序列

0/1揹包問題

0/1揹包問題可以看作是決策一個序列(x1, x2, …, xn),對任一變數xi的決策是決定xi=1還是xi=0。在對xi-1決策後,已確定了(x1, …, xi-1),在決策xi時,問題處於下列兩種狀態之一:

(1)揹包容量不足以裝入物品i,則xi=0,揹包不增加價值;

(2)揹包容量可以裝入物品i,則xi=1,揹包的價值增加了vi。

int v[maxn],w[maxn],dp[maxn];///dp[m]是揹包容量為m時能裝入的最大價值
int main()
{
    int t,i,j,n,m;
    cin>>t;
    while(t--)
    {
       cin>>n>>m;///n是物品數量,m是揹包容量
       for(i=1;i<=n;i++)
         cin>>v[i];///價值
       for(i=1;i<=n;i++)
         cin>>w[i];///體積
       memset(dp,0,sizeof(dp));
       for(i=1;i<=n;i++)
       {
          for(j=m;j>=w[i];j--)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
        cout<<dp[m]<<endl;
    }
}
0/1揹包

完全揹包

有n種物品和一個容量為v的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大

int dp[50010];
int w[2010],v[2010];///w是物品體積,v是物品價值
int main
{
  int i,j,t,n,m,a,b;
  scanf("%d",&t);
  while(t--)
  {
     scanf("%d%d",&n,&m);///n是物品數量,m是揹包容量
     for(i=0;i<n;i++)
       scanf("%d"%d",&w[i],&v[i]);
     memset(dp,-inf,sizeof(dp));
     dp[0]=0;
     for(i=0;i<n;i++)
     {
        for(j=w[i];j<=m;j++)
          dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
     }
     if(dp[m]>0) printf("%d\n",dp[m]);
     else printf("No\n");
  }  
  return 0;
}
完全揹包