[区间dp][前缀和] Jzoj P4517 病毒感染
题解
- 题目大意:有n个村庄,每天每个村庄会死ai个人。JYY每天可以选择把一个村庄的人治好或者是走一步,每当他往回走就要把前面所有没有治的村庄全部治好,问JYY要把所有人治好的死亡的最少人数
- 那么可以设f[i],为前i-1个村庄的人都治好了,现在在第i个村庄的死亡的最少人数,那么这个死亡的最少人数包括i-1个村庄的人也包括i~n村庄的人
- 转移的话,枚举一个最左没有被治好的村庄l,f[r]=min(f[l-1]+g[l][r]),其中g[i][j]为治好村庄l到r的死亡的最少人数
- 那么对于一条i到j的路径可以怎么走呢?
- 对于每一个村庄可以选择第一次经过的时候治好,也可以选择掉头经过的时候治好。那么这样的话,对于r后面的村庄,他们的贡献很好计算,是一个定值,那么考虑如何计算l~r这段的贡献
- 线段内的村庄就是就是只有两种情况,那么就可以通过g[l+1][r]转移过来,区间dp从小区间逐渐转移到大区间,就可以把所有情况都考虑完
- 那么这样的话,我们就可以计算出g数组的值了,那么就可以通过g数组来计算最后f数组的值
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #define N 3000 5 using namespace std; 6 int n; 7 long long g[N][N],a[N],f[N],sum[N]; 8 int main() 9 { 10 scanf("%d",&n),memset(f,125,sizeof(f)); 11 for (int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i]; 12 for (int i=1;i<=n;i++) g[i][i]=sum[n]-sum[i]; 13 for (int i=2,k;i<=n;i++) 14 for (int j=1;j<=n-i+1;j++) k=i+j-1,g[j][k]=min(g[j+1][k]+2*(sum[n]-sum[j])+sum[n]-sum[k],g[j+1][k]+sum[n]-sum[j-1]+(3*i-4)*a[j]+2*(sum[n]-sum[k])); 15 f[0]=0,f[1]=sum[n]-sum[1]; 16 for (int i=2;i<=n;i++) 17 { 18 f[i]=g[1][i]+(i-1)*(sum[n]-sum[i]); 19 for (int j=1;j<i;j++) f[i]=min(f[i],f[j]+sum[n]-sum[j]+g[j+1][i]+(i-j-1)*(sum[n]-sum[i])); 20 } 21 printf("%lld",f[n]); 22 }