紫书第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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n");
          else pf("%d\n",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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n", 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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",ispara(0,2));

          if(ispara(0,len-1))
          {
               pf("1\n");
               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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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.\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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("!\n")
#define INF 1<<30
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",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]);
          }
     }
}

 

posted @ 2016-03-12 17:44  qlky  阅读(429)  评论(0编辑  收藏  举报