• 紫书第9章 动态规划初步


    9.1 数字三角形

    9.1.2 记忆化搜索与递推

    方法1:递归计算。程序如下(需注意边界处理):

    int solve(int i,int j)
    {
         return a[i][j] + (i==n ?0:max(solve(i+1,j),solve(i+1,j+1));
    }

    用直接递归的方法计算状态转移方程,效率往往十分低下。其原因是相同的子问题被重复计算了多次。

    方法2:递推计算。程序如下(需再次注意边界处理):

    int i, j;
    for(j = 1; j <= n; j++) d[n][j] = a[n][j];
    for(i = n-1; i >= 1; i——)
    for(j = 1; j <= i; j++)
    d[i][j] = a[i][j] + max(d[i+1][j],d[i+1][j+1]);

    程序的时间复杂度显然是O(n2),但为什么可以这样计算呢?原因在于:i是 逆序枚举的,因此在计算d[i][j]前,它所需要的d[i+1][j]和d[i+1][j+1]一定已经计算出来了。

    方法3:记忆化搜索。程序分成两部分。首先用“memset(d,-1,sizeof(d));”把d全部初始化为-1,然后编写递归函数(1):

    int solve(int i, int j){
    if(d[i][j] >= 0) return d[i][j];
    return d[i][j] = a[i][j] + (i == n ? 0 : max(solve(i+1,j),solve(i+1,j+1)));

    9.2 DAG上的动态规划

    9.2.1 DAG模型
    嵌套矩形问题。有n个矩形,每个矩形可以用两个整数a、b描述,表示它的长和宽。矩
    形X(a,b)可以嵌套在矩形Y(c, d)中,当且仅当a<c,b<d,或者b<c,a<d(相当于把矩
    形X旋转90°)。例如,(1, 5)可以嵌套在(6, 2)内,但不能嵌套在(3, 4)内。你的任务是选出尽
    量多的矩形排成一行,使得除了最后一个之外,每一个矩形都可以嵌套在下一个矩形内。如
    果有多解,矩形编号的字典序应尽量小。

    int dp(int i)
    {
         int& ans = d[i];
         if(ans>0) return ans;
         for(int j = 0;j<n;j++)
         {
              if(G[i][j])
                   ans = max(ans,dp(j)+1);
         }
         return ans;
    }

    原题还有一个要求:如果有多个最优解,矩形编号的字典序应最小。还记得第6章中的
    例题“理想路径”吗?方法与其类似。将所有d值计算出来以后,选择最大d[i]所对应的i。如
    果有多个i,则选择最小的i,这样才能保证字典序最小。接下来可以选择d(i)=d(j)+1且
    (i,j)∈E的任何一个j。为了让方案的字典序最小,应选择其中最小的j

    void print_ans(int i) {2
    printf("%d ", i);
    for(int j = 1; j <= n; j++) if(G[i][j] && d[i] == d[j]+1){
    print_ans(j);
    break;
    }
    }

    9.2.3 固定终点的最长路和最短路

    int dp(int s)
    {
         int& ans = d[s];
         if(ans>=0) return ans;
         for(int i = 0;i<n;i++)
         {
              if(s>=v[i]) ans = max(ans,dp(s-v[i])+1);
         }
         return ans;
    }

    例题9-1 城市里的间谍(A Spy in the Metro, ACM/ICPC World Finals 2003,
    UVa1025)

    经典dp问题,和状态有关,数字又不大,可以直接dp递推。

    runtime_error有可能是指针越界,要注意

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 10000
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int t[205],d1[60],d2[60],has_train[205][60][2],dp[205][60];
    
    int main()
    {
         int i,j,T,M1,M2,n,kase=0;
        while(sf("%d",&n)==1 && n)
        {
            mem(dp,0);
            mem(d1,0);
            mem(d2,0);
            mem(has_train,0);
            mem(t,0);
            sf("%d",&T);
              for(i=0;i<n-1;i++)
                   sf("%d",&t[i]);
    
              sf("%d",&M1);
              for(i=0;i<M1;i++)
              {
                   sf("%d",&d1[i]);
                   int sum_time = 0;
                   has_train[d1[i]][0][0]=1;
                   for(j=0;j<n-1;j++)
                   {
                        sum_time+=t[j];
                        if(sum_time+d1[i]<=T)
                             has_train[sum_time+d1[i]][j+1][0]=1;
                        else
                             break;
                   }
              }
    
              sf("%d",&M2);
              for(i=0;i<M2;i++)
              {
                   sf("%d",&d2[i]);
                   has_train[d2[i]][n-1][1]=1;
                   int sum_time = 0;
                   for(j=n-2;j>=0;j--)
                   {
                        sum_time+=t[j];
                        if(d2[i]+sum_time<=T)
                             has_train[d2[i]+sum_time][j][1]=1;
                        else
                             break;
                   }
              }
    
              for(i=0;i<n-1;i++) dp[T][i] = INF;
              dp[T][n-1]=0;
    
              for(i=T-1;i>=0;i--)
              {
                   for(j=0;j<n;j++)
                   {
                        dp[i][j] = dp[i+1][j]+1;
                        if(j<n-1 && has_train[i][j][0] && i+t[j]<=T)
                             dp[i][j] = min(dp[i][j],dp[i+t[j]][j+1]);
                        if(j>0 && has_train[i][j][1] && i+t[j-1]<=T)
                             dp[i][j] = min(dp[i][j],dp[i+t[j-1]][j-1]);
                   }
              }
    
              pf("Case Number %d: ",++kase);
              if(dp[0][0]>=INF) pf("impossible
    ");
              else pf("%d
    ",dp[0][0]);
        }
    }

    递归版,测试结果对,不过WA了

    int re(int i,int j)
    {
         if(i==T) return dp[i][j];
         int& ans = dp[i][j];
         if(vis[i][j]) return ans;
    
         vis[i][j] = 1;
         ans = re(i+1,j)+1;
    
         for(int k = n-1;k>=0;k--)
         {
              if(k>0 && has_train[i][k][1] && i+t[k-1]<=T)
                   ans = min(ans,re(i+t[k-1],k-1));
              if(k<n-1 && has_train[i][k][0] && i+t[k]<=T)
                   ans = min(ans,re(i+t[k],k+1));
         }
         return ans;
    }

    例题9-2 巴比伦塔(The Tower of Babylon, UVa 437)

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 10000
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n;
    
    struct cube
    {
         int x,y,h;
         void setc(int a,int b,int c)
         {x=a;y=b;h=c;}
    }c[100];
    
    int g[100][100],d[100];
    
    int check(int i,int j)
    {
         if(c[i].x>c[j].x && c[i].y>c[j].y || c[i].x>c[j].y && c[i].y>c[j].x)
              return 1;
         return 0;
    }
    
    int dp(int i)
    {
         int& ans = d[i];
    
         if(ans>0) return ans;
    
         ans = c[i].h;
    
         for(int j =0;j<n;j++)
         {
              if(g[i][j]) ans = max(ans,dp(j)+c[i].h);
         }
         return ans;
    }
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d",&n)==1 && n)
        {
            mem(d,0);
            mem(c,0);
            mem(g,0);
            for(i=0;i<n;i++)
              {
                   int a,b,d;
                   sf("%d %d %d",&a,&b,&d);
                   c[i].setc(a,b,d);
                   c[i+n].setc(b,d,a);
                   c[i+2*n].setc(d,a,b);
              }
              n*=3;
    
              for(i=0;i<n;i++)
              {
                   for(j=i+1;j<n;j++)
                   {
                        g[i][j] = check(i,j);
                        g[j][i] = check(j,i);
                   }
              }
              int res = 0;
              for(i=0;i<n;i++)
                   res = max(res,dp(i));
              printf("Case %d: maximum height = %d
    ", kase++, res);
        }
    }

    递推:

              sort(c,c+n,cmp);
    
              for(i=0;i<n;i++)
              {
                   for(j=i+1;j<n;j++)
                   {
                        g[i][j] = check(i,j);
                        g[j][i] = check(j,i);
                   }
              }
    
              for(i=0;i<n;i++)
                   d[i] = c[i].h;
    
    
              for(i=0;i<n;i++)
              {
                   int tmp = 0;
                   for(j=i;j<n;j++)
                   {
                        if(g[j][i])
                        {
                             d[j] = max(d[j],d[i]+c[j].h);
                        }
                   }
    
              }
    
              int res = 0;
    
              for(i=0;i<n;i++)
              {
                   if(res<d[i]) res=d[i];
              }

    例题9-3 旅行(Tour, ACM/ICPC SEERC 2005, UVa1347)

    注意状态的设定能使问题简单化

    另外这题算法导论里也有,叫双调欧几里得旅行商问题

    用的是递推的方法:http://blog.csdn.net/xiajun07061225/article/details/8092247

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 10000
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n;
    
    double d[1000][1000];
    
    struct point
    {
         int x,y;
    }p[1000];
    
    double getd(int a,int b)
    {
         return sqrtf((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
    }
    
    double dp(int i,int j)
    {
         double &ans = d[i][j];
         if(ans>0) return ans;
         if(i==n-2)
         {
              return getd(n-2,n-1)+getd(j,n-1);
         }
         ans = min(dp(i+1,j)+getd(i,i+1),dp(i+1,i)+getd(j,i+1));
         return ans;
    }
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d",&n)==1 && n)
        {
            mem(d,0);
            mem(p,0);
            for(i=0;i<n;i++)
                   sf("%d%d",&p[i].x,&p[i].y);
              pf("%.2lf
    ",dp(1,0)+getd(1,0));
        }
    }

    递推:

    分类讨论,状态递推

    int main()
    {
         int i,j,kase=1;
        while(sf("%d",&n)==1 && n)
        {
            mem(d,0);
            mem(p,0);
            for(i=0;i<n;i++)
                   sf("%d%d",&p[i].x,&p[i].y);
    
            d[1][0] = getd(0,1);
            for(i=2;i<n;i++)
            {
                 for(j=0;j<=i-2;j++)
                 {
                      d[i][j] = d[i-1][j] + getd(i,i-1);
                 }
                 d[i][i-1] = INF;
    
                 for(j=0;j<=i-2;j++)
                 {
                      d[i][i-1] = min(d[i][i-1],d[i-1][j]+getd(j,i));
                 }
            }
            d[n-1][n-1] = d[n-1][n-2]+getd(n-1,n-2);
    
            pf("%.2lf
    ",d[n-1][n-1]);
        }
    }

    例题9-4 单向TSP(Unidirectional TSP, UVa 116)

    字典序最小,所以递推比较好,遍历时注意按顺序遍历

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    int mp[15][105],d[15][105],nt[105][105];
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d%d",&n,&m)==2)
        {
    
            for(i=0;i<n;i++)
              {
                   for(j=0;j<m;j++)
                   {
                        sf("%d",&mp[i][j]);
                   }
              }
    
              int ans = INF,first = 0;
    
              for(j=m-1;j>=0;j--)
              {
                   for(i=0;i<n;i++)
                   {
                        if(j==m-1) d[i][j] = mp[i][j];
                        else
                        {
                             int row[3]={i-1,i,i+1};
                             if(i==0) row[0] = n-1;
                             if(i==n-1) row[2] = 0;
                             sort(row,row+3);
                             d[i][j] = INF;
                             for(int k = 0;k<3;k++)
                             {
                                  int v = d[row[k]][j+1]+mp[i][j];
                                  if(v<d[i][j])
                                  {
                                       d[i][j] = v;
                                       nt[i][j] = row[k];
                                  }
                             }
                        }
                        if(j==0 && d[i][j]<ans)
                        {
                             first = i;
                             ans = d[i][j];
                        }
                   }
              }
    
    //          for(i=0;i<n;i++)
    //          {
    //               for(j=0;j<m;j++)
    //               {
    //                    pf("%lld ",d[i][j]);
    //               }
    //               blank;
    //          }
              pf("%d",first+1);
              int v = nt[first][0];
              for(int i = nt[first][0], j = 1; j < m; i = nt[i][j], j++)
                   printf(" %d", i+1);
              blank;
              pf("%d
    ",ans);
        }
    }

    递归:

    int n,m;
    
    int mp[15][105],d[15][105],nt[105][105];
    
    int dp(int i,int j)
    {
         int& ans = d[i][j];
    
         if(j==m-1) return mp[i][j];
    
         if(ans>0) return d[i][j];
    
         int row[3]={i-1,i,i+1};
         if(i==0) row[0] = n-1;
         if(i==n-1) row[2] = 0;
         sort(row,row+3);
    
         ans = INF;
    
         for(int k = 0;k<3;k++)
         {
              ans = min(ans,dp(row[k],j+1)+mp[i][j]);
         }
         return ans;
    }
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d%d",&n,&m)==2)
        {
    
            for(i=0;i<n;i++)
              {
                   for(j=0;j<m;j++)
                   {
                        sf("%d",&mp[i][j]);
                   }
              }
              mem(d,0);
              int res =INF;
              for(i=0;i<n;i++)
                   res = min(res,dp(i,0));
              pf("%d
    ",res);
        }
    }

    例题9-5 劲歌金曲(Jin Ge Jin Qu [h]ao, Rujia Liu's Present 6, UVa 12563)

    这题是01变种,因为要求时间长度,即满足最大值时的体积。所以需要给01加一个限定条件

    v==weight || dp[v-weight]>=1
    1可以替换成val
    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,t,res;
    
    int song[60],dp[100000];
    
    void zobag(int weight)
    {
         int v;
         for(v=t;v>=weight;v--)
         {
              if(v==weight || dp[v-weight]>=1)
              {
                   dp[v] = max(dp[v],dp[v-weight]+1);
                   res = max(res,dp[v]);
              }
         }
    }
    
    
    int main()
    {
         int i,j,kase=1;
        int T;
        sf("%d",&T);
        while(T--)
         {
              sf("%d%d",&n,&t);
              t--;
              mem(dp,0);
              res = 0;
    
              for(i=0;i<n;i++)
                   sf("%d",&song[i]);
    
              for(i=0;i<n;i++)
              {
                   zobag(song[i]);
              }
    
              int x;
              for(i=t;i>=0;i--)
              {
                   if(dp[i]==res)
                   {
                        x=i;
                        break;
                   }
              }
              pf("Case %d: %d %d
    ",kase++,res+1,678+x);
         }
    }

    最长上升子序列(LIS)

    nlgn算法:http://blog.csdn.net/dangwenliang/article/details/5728363

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n;
    
    int num[100],dp[100],pre[100];
    
    int main()
    {
         int i,j,kase=1;
         while(sf("%d",&n)==1 && n)
         {
              for(i=0;i<n;i++)
                   sf("%d",&num[i]);
    
              for(i=0;i<n;i++) dp[i] = 1;
    
              int res = 0,v=0;
    
              for(i=0;i<n;i++)
              {
                   for(j=0;j<i;j++)
                   {
                        if(num[i]>num[j])
                        {
                             int t = dp[j]+1;
                             if(t>dp[i])
                             {
                                  dp[i] = t;
                                  pre[i] = j;
                             }
                        }
                   }
              }
              for(i=0;i<n;i++)
              {
                   if(dp[i]>res)
                   {
                        res = dp[i];
                        v = i;
                   }
              }
              pf("%d ",num[v]);
              for(i=1;i<res;i++)
              {
                   pf("%d ",num[pre[v]]);
                   v = pre[v];
              }
              blank;
              pf("%d
    ",res);
    
         }
    }
    /*
    6
    1 6 2 3 7 5
    */

    最长公共子序列(LCS)

    递归:

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m,res;
    
    int a[10000],b[10000];
    
    int dp(int i,int j)
    {
         if(i>=n || j>=m) return 0;
         if(a[i]==b[j]) return dp(i+1,j+1)+1;
         else
         {
              return max(dp(i,j+1),dp(i+1,j));
         }
    }
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d%d",&n,&m)==2)
         {
              for(i=0;i<n;i++)
                   sf("%d",&a[i]);
              for(i=0;i<m;i++)
                   sf("%d",&b[i]);
              pf("%d
    ",dp(0,0));
         }
    }
    /*
    6 7
    1 5 2 6 8 7
    2 3 5 6 9 8 4
    */

    递推:

    判断条件要稍微注意一下,递推时可以用flag来保存数组,见http://www.cnblogs.com/xudong-bupt/archive/2013/03/15/2959039.html

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    int a[1000],b[1000],dp[1000][1000],flag[1000][1000],res[1000];
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d%d",&n,&m)==2)
         {
              for(i=0;i<n;i++)
                   sf("%d",&a[i]);
              for(i=0;i<m;i++)
                   sf("%d",&b[i]);
    
              for(i=1;i<=n;i++)
              {
                   for(j=1;j<=m;j++)
                   {
                        if(a[i-1]==b[j-1])
                        {
                             dp[i][j] = dp[i-1][j-1]+1;
                             flag[i][j] = 1;//斜上
                        }
                        else if(dp[i-1][j]>dp[i][j-1])
                        {
                             dp[i][j] = dp[i-1][j];
                             flag[i][j] = 2;//
                        }
                        else
                        {
                             dp[i][j] = dp[i][j-1];
                             flag[i][j] = 3;//
                        }
                   }
              }
              i = n;
              j = m;
              int k = 0;
              while(i>0 && j>0)
              {
                   if(flag[i][j]==1)
                   {
                        res[k++]=a[i-1];
                        i--;
                        j--;
                   }
                   else if(flag[i][j]==2)
                        i--;
                   else
                        j--;
              }
              for(i=k-1;i>=0;i--)
                   pf("%d ",res[i]);
              blank;
              pf("%d
    ",dp[n][m]);
         }
    }
    /*
    6 7
    1 5 2 6 8 7
    2 3 5 6 9 8 4
    4 5
    1 3 7 5
    3 4 6 7 5
    */

    例题9-6 照明系统设计(Lighting System Design, UVa 11400)

    当前决策影响后面决策,所以可以用分段更新。见:http://blog.csdn.net/yanzheshi/article/details/47069189

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    int s[1005],dp[1005];
    
    struct light
    {
         int V,K,C,L;
    }lt[1005];
    
    int cmp(const light& a,const light& b)
    {
         return a.V<b.V;
    }
    
    int main()
    {
         int i,j,kase=1;
        while(sf("%d",&n)==1 && n)
         {
              int k = 1,sum=0;
              for(i=0;i<n;i++)
              {
                   sf("%d%d%d%d",&lt[i].V,&lt[i].K,&lt[i].C,&lt[i].L);
              }
              sort(lt,lt+n,cmp);
              for(i=0;i<n;i++)
              {
                   sum+=lt[i].L;
                   s[k++]=sum;
              }
    
              for(i=0;i<n;i++)
              {
                   dp[i] = s[i+1]*lt[i].C+lt[i].K;
                   for(j=0;j<i;j++)
                   {
                        dp[i] = min(dp[i],dp[j]+(s[i+1]-s[j+1])*lt[i].C+lt[i].K);
                   }
              }
              pf("%d
    ",dp[n-1]);
    
         }
    }

    例题9-7 划分成回文串(Partitioning by Palindromes, UVa 11584)

    dp[i]为i长度的最小值,dp[j]已知时,判断后面j+1-i的状态,若为回文,则dp[i]=dp[j]+1

    判断回文这里给了个更简便的方法:http://blog.csdn.net/shuangde800/article/details/9669175

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    char str[1005];
    int dp[1005];
    
    int ispara(int a,int b)
    {
         if(a==b) return 1;
         int mid = (a+b)>>1;
         int l = b-a+1,i;
         if(l%2==0)
         {
              for(i=0;i<l/2;i++)
              {
                   if(str[mid-i]!=str[mid+i+1]) return 0;
              }
         }
         else
         {
              for(i=1;i<=l/2;i++)
              {
                   if(str[mid-i]!=str[mid+i]) return 0;
              }
         }
         return 1;
    }
    
    int main()
    {
         int i,j,T;
         sf("%d",&T);
        while(T--)
         {
              sf("%s",str);
              int len = strlen(str);
    //          pf("%d
    ",ispara(0,2));
    
              if(ispara(0,len-1))
              {
                   pf("1
    ");
                   continue;
              }
    
              for(i=0;i<len;i++)
              {
                   dp[i]=i+1;
                   if(ispara(0,i))
                   {
                        dp[i]=1;
                        continue;
                   }
                   for(j=0;j<i;j++)
                   {
                        if(ispara(j+1,i))
                             dp[i] = min(dp[i],dp[j]+1);
                   }
              }
              pf("%d
    ",dp[len-1]);
         }
    }

    例题9-8 颜色的长度(Color Length, ACM/ICPC Daejeon 2011, UVa1625)

    http://blog.csdn.net/chlerry/article/details/48322275#

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define mp make_pair
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    char a[5005],b[5005];
    int dp[1005][1005],res[1005][1005],sta[27],stb[27],eda[27],edb[27];
    
    int main()
    {
         int i,j,T;
         sf("%d",&T);
        while(T--)
         {
              sf("%s%s",a,b);
              int al = strlen(a);
              int bl = strlen(b);
    
              mem(sta,-1);
              mem(stb,-1);
              mem(eda,-1);
              mem(edb,-1);
    
              for(i=0;i<al;i++)
              {
                   if(sta[a[i]-'A']==-1) sta[a[i]-'A'] =i;
                   eda[a[i]-'A'] = i;
              }
    
              for(i=0;i<bl;i++)
              {
                   if(stb[b[i]-'A']==-1) stb[b[i]-'A'] =i;
                   edb[b[i]-'A'] = i;
              }
    
              for(i=0;i<=al;i++)
              {
                   for(j=0;j<=bl;j++)
                   {
                        dp[i][j] = 0;
                        for(int k =0;k<26;k++)
                        {
                             if((i>sta[k] && i<eda[k]) || (i>stb[k] && i<edb[k]))
                                  dp[i][j]++;
                        }
                        if(i==0&&j==0) continue;
                        else if(i==0)
                             dp[i][j]+=dp[i][j-1];
                        else if(j==0)
                             dp[i][j]+=dp[i-1][j];
                        else
                             dp[i][j] += min(dp[i-1][j],dp[i][j-1]);
                   }
              }
              pf("%d
    ",dp[al-1][bl]);
    
    
         }
    }

    最优矩阵链乘

    递归法,分析见:

    http://blog.jobbole.com/87012/

    http://blog.csdn.net/simmerlee/article/details/7731594

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define mp make_pair
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    typedef pair<int,int> pa;
    vector<pa> p;
    
    int f[100][100];
    
    int dp(int i,int j)
    {
         if(f[i][j]!=-1) return f[i][j];
    
         if(i==j) return f[i][j]=0;
    
         f[i][j] = INF;
    
         for(int k=i;k<j;k++)
         {
              f[i][j] = min(f[i][j],dp(i,k)+dp(k+1,j)+p[i].first*p[k].second*p[j].second);
         }
         return f[i][j];
    }
    
    int main()
    {
         int i,j,T;
         while(sf("%d",&n)==1)
         {
              p.clear();
              for(i=0;i<n;i++)
              {
                   int a,b;
                   sf("%d%d",&a,&b);
                   p.pb(mp(a,b));
              }
              mem(f,-1);
              pf("%d
    ",dp(0,n-1));
         }
    }
    /*
    3
    2 3
    3 4
    4 5
    6
    30 35
    35 15
    15 5
    5 10
    10 20
    20 25
    */

     递推:http://blog.csdn.net/simmerlee/article/details/7731594

    上述方程有些特殊:记忆化搜索固然没问题,但如果要写成递推,无论
    按照i还是j的递增或递减顺序均不正确。正确的方法是按照j-i递增的顺序递推,因为长区
    间的值依赖于短区间的值。

    for(r=1;r<n;i++)
              {
                   for(i=1;i<=n-r+1;i++)
                   {
                        j = i+r;
                        f[i][j] = f[i+1][j]+p[i-1]*p[i]*p[j];
                        s[i][j] = i;
                        
                        for(int k = i+1;k<j;k++)
                        {
                             int t = f[i][k]+f[k+1][j]+p[i-1]*p[k]*p[j];
                             if(t<f[i][j])
                             {
                                  f[i][j] = t;
                                  s[i][j] = k;
                             }
                        }
                   }
              }

    UVA 348

    记录路径的方法:用r[i][j]保存断点,然后递归输出路径

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define mp make_pair
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int n,m;
    
    typedef pair<int,int> pa;
    vector<pa> p;
    
    int f[100][100],r[100][100];
    
    int dp(int i,int j)
    {
         if(f[i][j]!=-1) return f[i][j];
         r[i][j]=i;
         if(i==j) return f[i][j]=0;
    
         f[i][j] = INF;
    
         for(int k=i;k<j;k++)
         {
              int v = dp(i,k)+dp(k+1,j)+p[i].first*p[k].second*p[j].second;
              if(v<f[i][j])
              {
                   f[i][j] = v;
                   r[i][j] = k;
              }
         }
         return f[i][j];
    }
    
    void print(int i,int j)
    {
         if(i>j) return;
         if(i==j) pf("A%d",i+1);
         else
         {
              pf("(");
              print(i,r[i][j]);
              pf(" x ");
              print(r[i][j]+1,j);
              pf(")");
         }
    }
    
    int main()
    {
         int i,j,T,kase=0;
         while(sf("%d",&n)==1 && n)
         {
              p.clear();
              for(i=0;i<n;i++)
              {
                   int a,b;
                   sf("%d%d",&a,&b);
                   p.pb(mp(a,b));
              }
              mem(f,-1);
              pf("%d
    ",dp(0,n-1));
              pf("Case %d: ",++kase);
              print(0,n-1);
              blank;
         }
    }
    /*
    3
    2 3
    3 4
    4 5
    6
    30 35
    35 15
    15 5
    5 10
    10 20
    20 25
    */

    例题9-9 切木棍(Cutting Sticks, UVa 10003)

    这题让我更加明确了DP的使用状态

    一:重叠子问题

    二:最优子结构

    既然是最优子结构,肯定要从最小结构入手,一直推到最后的情况

    这里的最下结构就是间距为1时,所以不是用i,j枚举

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define mp make_pair
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    int d[60],dp[60][60];
    
    int main()
    {
         int L;
         while(sf("%d",&L)==1 && L)
         {
              int n,i,j,r,k;
              sf("%d",&n);
              for(i=1;i<=n;i++)
                   sf("%d",&d[i]);
              d[0] = 0;
              d[n+1] = L;
    
              for(r=1;r<=n+1;r++)
              {
                   for(i=0;i<=n+1;i++)
                   {
                        j = i+r;
                        if(j>n+1) break;
                        int tmp = INF;
                        for(k=i+1;k<j;k++)
                        {
                             int t = dp[i][k]+dp[k][j]+d[j]-d[i];
                             if(t<tmp) tmp = t;
                        }
                        if(tmp!=INF) dp[i][j] = tmp;
                   }
              }
    
              pf("The minimum cutting is %d.
    ",dp[0][n+1]);
         }
    }

     递推:

    int re(int i,int j)
    {
         if(i==j-1) return 0;
    
         int& ans = dp[i][j];
    
         if(ans) return ans;
    
         ans = INF;
    
         for(int k = i+1;k<j;k++)
         {
              ans = min(ans,re(i,k)+re(k,j)+ d[j]-d[i]);
         }
         return ans;
    }

    例题9-10 括号序列(Brackets Sequence, NEERC 2001, UVa1626)

    还是最小到最大,所以i逆着枚举 

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define mp make_pair
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    char str[250];
    
    int dp[250][250];
    
    int match(int i,int j)
    {
         return (str[i]=='[' && str[j]==']') || (str[i]=='(' && str[j]==')');
    }
    
    void print(int i,int j)
    {
         int k;
         if(i>j) return;
         if(i==j)
         {
              if(str[i]=='(' || str[i]==')') pf("()");
              else pf("[]");
              return;
         }
    
         int ans = dp[i][j];
         if(match(i,j) && ans == dp[i+1][j-1])
         {
              pf("%c",str[i]);
              print(i+1,j-1);
              pf("%c",str[j]);
              return;
         }
         for(k=i;k<j;k++)
         {
              if(ans == dp[i][k]+dp[k+1][j])
              {
                   print(i,k);
                   print(k+1,j);
                   return;
              }
         }
    }
    
    int main()
    {
         int T,r,i,j,k;
         sf("%d",&T);
         while(T--)
         {
              sf("%s",str);
              int n = strlen(str);
    
              for(i=0;i<n;i++)
              {
                   dp[i][i] = 1;
              }
    
              for(i=n-2;i>=0;i--)
              {
                   for(j=i+1;j<n;j++)
                   {
                        dp[i][j] = n;
                        if(match(i,j))
                        {
                             dp[i][j] = min(dp[i][j],dp[i+1][j-1]);
                        }
                        for(k=i;k<j;k++)
                        {
                             dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]);
                        }
                   }
              }
              pf("%d
    ",dp[0][n-1]);
              print(0,n-1);
              blank;
         }
    }
    /*
    4
    ([(]
    ([)]
    ([])
    (((]]
    */

    递归:

    递归如果要打印路径记得不要只返回一个值,这样边界条件的数组就不会保存

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define mp make_pair
    #define debug printf("!
    ")
    #define INF 1<<30
    #define MAXN 5010
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    
    char str[250];
    
    int dp[250][250];
    
    int match(int i,int j)
    {
         return (str[i]=='[' && str[j]==']') || (str[i]=='(' && str[j]==')');
    }
    
    int re(int i,int j)
    {
         int& ans = dp[i][j];
         if(i==j) return ans = 1;
         if(ans) return ans;
         ans = INF;
         if(match(i,j)) ans = min(ans,re(i+1,j-1));
    
         for(int k = i;k<j;k++)
              ans = min(ans,re(i,k)+re(k+1,j));
         return ans;
    }
    
    void print(int i,int j)
    {
         int k;
         if(i>j) return;
         if(i==j)
         {
              if(str[i]=='(' || str[i]==')') pf("()");
              else pf("[]");
              return;
         }
    
         int ans = dp[i][j];
         if(match(i,j) && ans == dp[i+1][j-1])
         {
              pf("%c",str[i]);
              print(i+1,j-1);
              pf("%c",str[j]);
              return;
         }
         for(k=i;k<j;k++)
         {
              if(ans == dp[i][k]+dp[k+1][j])
              {
                   print(i,k);
                   print(k+1,j);
                   return;
              }
         }
    }
    
    int main()
    {
         int T,r,i,j,k;
         sf("%d",&T);
         while(T--)
         {
              sf("%s",str);
              int n = strlen(str);
              mem(dp,0);
              pf("%d
    ",re(0,n-1));
              print(0,n-1);
              blank;
         }
    }
    /*
    4
    ([(]
    ([)]
    ([])
    (((]]
    */

    例题9-11 最大面积最小的三角剖分(Minimax Triangulation, ACM/ICPC NWERC
    2004, UVa1331)

    9.4.2 树上的动态规划

    树的最大独立集

    int dp(int x,int fa)
    {
         for(int i=0;i<edge[x].size();i++)
         {
              int m = edge[x][i];
              if(m!=fa)
              {
    
              }
         }
    }
    
    int main()
    {
         int T,r,i,j,n;
         while(sf("%d",&n)==1 && n)
         {
              string a,b;
              cin>>a;
              int k = 0;
              name.insert(mp(a,k++));
              for(i=0;i<n-1;i++)
              {
                   cin>>a>>b;
                   if(!name.count(a)) name.insert(mp(a,k++));
                   if(!name.count(b)) name.insert(mp(b,k++));
                   edge[name[a]].pb(name[b]);
                   edge[name[b]].pb(name[a]);
              }
         }
    }
  • 相关阅读:
    JS时钟--星期 年 月 日 时 分
    [考试反思]0825NOIP模拟测试30:没落
    [考试反思]0822NOIP模拟测试29:延续
    [考试反思]0821NOIP模拟测试28:沉默
    小奇的仓库:换根dp
    短期Flag
    [考试反思]0820NOIP模拟测试27:幻影
    [考试反思]0819NOIP模拟测试26:荒芜
    0818NOIP模拟测试25——B卷简记
    [模板]tarjan——最后通牒
  • 原文地址:https://www.cnblogs.com/qlky/p/5269474.html
Copyright © 2020-2023  润新知