题意:在给出的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