题意:在给出的n个结点处切断木棍,并且在切断木棍时木棍有多长就花费多长的代价,将所有结点切断,并且使代价最小。
思路:设DP[i][j]为,从i,j点切开的木材,完成切割需要的cost,显然对于所有DP[i][i+1]=0,记w[i][j]为从i,j点切开的木材的长度,因此可以枚举切割点,dp[i][j]=min(dp[i][k]+dp[k][j])+w[i][j],k就是枚举的切割点.
AC代码:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 const int SIZEN=55;
6 const int INF=1<<30;
7 int x[SIZEN];
8 int dp[SIZEN][SIZEN];
9 int w[SIZEN][SIZEN];
10 void init()
11 {
12 memset(dp,-1,sizeof(dp));
13 }
14 int dfs(int l,int r)
15 {
16 if(dp[l][r]!=-1)
17 return dp[l][r];
18 int ans=INF;
19 for(int i=l+1; i<r; i++)
20 {
21 ans=min(ans,dfs(l,i)+dfs(i,r));
22 }
23 dp[l][r]=ans+w[l][r];
24 return dp[l][r];
25 }
26 void solve(int len)
27 {
28 int n;
29 scanf("%d",&n);
30 init();
31 for(int i=1; i<=n; i++)
32 scanf("%d",&x[i]);
33 x[0]=0;
34 x[n+1]=len;
35 for(int i=0; i<=n; i++)
36 for(int j=i; j<=n+1; j++)
37 w[i][j]=x[j]-x[i];
38 for(int i=0; i<=n; i++)
39 dp[i][i+1]=0;
40 int ans=dfs(0,n+1);
41 printf("The minimum cutting is %d.\n",ans);
42 }
43 int main()
44 {
45 int len;
46 while(scanf("%d",&len)!=EOF&&len)
47 solve(len);
48 }
这道题涉及到一个知识点:区间DP:(转自calmound)
区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值。
设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价
最小区间F[i,i]=0(一个数字无法合并,∴代价为0)
每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段
For p:=1 to n do // p是区间长度,作为阶段。
for i:=1 to n do // i是穷举的区间的起点
begin
j:=i+p-1; // j是 区间的终点,这样所有的区间就穷举完毕
if j>n then break; // 这个if很关键。
for k:= i to j-1 do // 状态转移,去推出 f[i,j]
f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] }
end;
这个结构必须记好,这是区间动态规划的代码结构。
下面是关于该题的一个优化代码(四边形优化),我还没搞懂,先贴出来吧:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 const int SIZEN=55;
6 const int INF=1<<30;
7 int x[SIZEN];
8 int dp[SIZEN][SIZEN];
9 int w[SIZEN][SIZEN];
10 int s[SIZEN][SIZEN];
11 //dp[i][j]=min(dp[i][k]+dp[k][j])+w[i][j]
12 void init(){
13 memset(dp,-1,sizeof(dp));
14 }
15 void work(int n){
16 for(int i=0;i<=n;i++) dp[i][i+1]=0;
17 for(int i=0;i<=n;i++) s[i][i+1]=i;
18 for(int l=3;l<=n+2;l++){
19 for(int i=0;i+l-1<=n+1;i++){
20 int j=i+l-1;
21 dp[i][j]=INF;
22 for(int k=s[i][j-1];k<=s[i+1][j];k++){
23 int tmp=dp[i][k]+dp[k][j];
24 if(dp[i][j]>tmp){
25 dp[i][j]=tmp;
26 s[i][j]=k;
27 }
28 }
29 dp[i][j]+=w[i][j];
30 }
31 }
32 }
33 void solve(int len){
34 int n;
35 scanf("%d",&n);
36 for(int i=1;i<=n;i++) scanf("%d",&x[i]);
37 x[0]=0;x[n+1]=len;
38 for(int i=0;i<=n;i++)
39 for(int j=i;j<=n+1;j++)
40 w[i][j]=x[j]-x[i];
41 work(n);
42 /*for(int i=0;i<=n+1;i++){
43 for(int j=i+1;j<=n+1;j++) printf("%d ",dp[i][j]);
44 printf("\n");
45 }
46 for(int i=0;i<=n+1;i++){
47 for(int j=i+1;j<=n+1;j++)
48 printf("%d ",s[i][j]);
49 printf("\n");
50 }*/
51 printf("The minimum cutting is %d.\n",dp[0][n+1]);
52 }
53 int main()
54 {
55 int len;
56 while(scanf("%d",&len)!=EOF&&len)
57 solve(len);
58 }
本篇博文并非出自本人之手,只是做个总结,感谢ACalvin男神的帮助。
来源:https://www.cnblogs.com/PJQOOO/p/3898552.html