UVA 10003 Cutting Sticks

题意:在给出的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男神的帮助。

posted @ 2014-08-08 08:44  PJQOOO  阅读(272)  评论(0编辑  收藏  举报