有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。
n<=3000
普通dp:
- #include <iostream>
- using namespace std;
- #define M 3001
- #define INF 1000000000
- int n,f[M][M],sum[M][M],stone[M];
- int main()
- {
- int i,j,k,t;
- cin>>n;
- for(i=1;i<=n;i++)
- scanf("%d",&stone[i]);
- for(i=1;i<=n;i++)
- {
- f[i][i]=0;
- sum[i][i]=stone[i];
- for(j=i+1;j<=n;j++)
- sum[i][j]=sum[i][j-1]+stone[j];
- }
- for(int len=2;len<=n;len++)//归并的石子长度
- {
- for(i=1;i<=n-len+1;i++)//i为起点,j为终点
- {
- j=i+len-1;
- f[i][j]=INF;
- for(k=i;k<=j-1;k++)
- {
- if(f[i][j]>f[i][k]+f[k+1][j]+sum[i][j])
- f[i][j]=f[i][k]+f[k+1][j]+sum[i][j];
- }
- }
- }
- printf("%d/n",f[1][n]);
- return 0;
- }
由范围可知,用普通的区间型dp会TLE,所以,必须要优化。
单调性。
动态规划也有单调性??
- #include <iostream>
- using namespace std;
- #define M 3001
- #define INF 1000000000
- int n,f[M][M],sum[M][M],stone[M],s[M][M];
- int main()
- {
- int i,j,k,t;
- cin>>n;
- for(i=1;i<=n;i++)
- scanf("%d",&stone[i]);
- for(i=1;i<=n;i++)
- {
- f[i][i]=0;
- s[i][i]=i;
- sum[i][i]=stone[i];
- for(j=i+1;j<=n;j++)
- sum[i][j]=sum[i][j-1]+stone[j];
- }
- for(int len=2;len<=n;len++)//归并的石子长度
- {
- for(i=1;i<=n-len+1;i++)//i为起点,j为终点
- {
- j=i+len-1;
- f[i][j]=INF;
- for(k=s[i][j-1];k<=s[i+1][j];k++)
- {
- if(f[i][j]>f[i][k]+f[k+1][j]+sum[i][j])
- {
- f[i][j]=f[i][k]+f[k+1][j]+sum[i][j];
- s[i][j]=k;
- }
- }
- }
- }
- printf("%d/n",f[1][n]);
- return 0;
- }