今天写内网题,连着写了两道区间dp,这里就总结一下。
区间dp思想主要是先枚举f[i][j]中的i,再枚举j,再枚举一个1~j之间的变量k,一般是f[i][j] = max(f[i][j],f[i][k] + f[k][j]);(石子合并)
但是今天遇到的两个都不是这样的。
第一题,复制书稿,洛谷P1282。猜到是区间dp了,但是没写出来。后来看了一下,f[i][j]代表前i个人写到j本书,枚举k为第i个人从第k本书开始写。这样转移方程就很好想了。f[i][j] = min(f[i][j],max(f[i - 1][l],a[l - 1] + a[l] + a[l + 1] + ... + a[j]),这样复杂度有点高,所以后半部分用前缀和来维护就行了。
代码:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int f[510][510]; int a[510],m,n,sum[510]; void print(int x, int Ans) { if(!x) return; for(int i=x; i>=0; i--) { if(sum[x] - sum[i-1] > Ans || !i) { print(i, Ans); printf("%d %d ", i+1, x); break; } } } int main() { scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++) { scanf("%d",&a[i]); } memset(f,127,sizeof(f)); for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i],f[1][i] = sum[i]; for(int i = 2; i <= m; i++) { for(int j = i; j <= n; j++) { for(int l = i; l <= j; l++) { f[i][j] = min(f[i][j],max(f[i - 1][l - 1],sum[j] - sum[l - 1])); } } } print(n,f[m][n]); return 0; } /* 9 3 1 2 3 4 5 6 7 8 9 */
第二题,机器分配,到现在我也不知道为什么是区间dp,但是也只能硬着头皮写了。
第一次,想正常思路,f[i][j]代表i个公司分配j个机器。中间枚举第i个公司分配k个机器。但是貌似过不去,只能的90,因为要按照字典序输出。这个题需要倒着枚举。
#include<cstdio> #include<iostream> using namespace std; int f[50][50],path[15][20][15]; int num[50][50],n,m; int main() { scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++) { for(int j = 1;j <= m;j++) scanf("%d",&num[i][j]); } for(int i = 1;i <= n;i++) { for(int j = 0;j <= m;j++) { for(int k = 0;k <= j;k++) { if(f[i - 1][j - k] + num[i][k] > f[i][j]) { f[i][j] = f[i - 1][j - k] + num[i][k]; for(int h = 1;h < i;h++) path[i][j][h] = path[i - 1][j - k][h]; path[i][j][i] = k; } } } } cout<<f[n][m]<<endl; for(int i = 1;i <= n;i++) { cout<<i<<" "<<path[n][m][i]<<endl; } return 0; }
倒着枚举就是f[i][j]代表i各公司不给j个,然后就好了。
#include<cstdio> #include<iostream> using namespace std; int f[50][50],path[15][20][15]; int num[50][50],n,m; int main() { scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++) { for(int j = 1;j <= m;j++) scanf("%d",&num[i][j]); } for(int i = 1;i <= n;i++) { for(int j = 0;j <= m;j++) { for(int k = 0;k <= j;k++) { if(f[i - 1][k] + num[i][j - k] > f[i][j]) { f[i][j] = f[i - 1][k] + num[i][j - k]; for(int h = 1;h < i;h++) path[i][j][h] = path[i - 1][k][h]; path[i][j][i] = j - k; } } } } cout<<f[n][m]<<endl; for(int i = 1;i <= n;i++) { cout<<i<<" "<<path[n][m][i]<<endl; } return 0; }