您的位置:首页 > 其它

紫书第9章 动态规划初步

2016-03-12 17:44 330 查看

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
[j] = a
[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
[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",<[i].V,<[i].K,<[i].C,<[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]);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: