题目大意:医疗团队来到n个村庄,n个村庄依次标号为1,2,…n。第i个村庄有ai个感染者。从1出发,每天可以去相邻村庄/治愈所在村庄,如果第i个村庄没有被治疗,那么每天这个村庄会死去ai个人,医疗团队在经过村庄i时,可能选择不治疗这个村庄而前往下一个村庄i+1,但是下次返回需要治疗之前所有未治疗的村庄,求最小死亡人数
题解:g[i][j]表示从j开始走,走到i,然后回头到j,i到j之间的村子的最小死亡人数,g[i][i]=0,考虑j这个村子一开始是治疗还是被跳过,跳过的话路程消耗3∗(i−j)天,每天村庄j死亡a[j]的人,治疗的话消耗1天,死亡sum[j+1,i]的人
g[i][j]=g[i][j+1]+sum[j+1,i]+min((i−j)∗3∗a[j],sum[j+1,i])
f[i]表示走到i,且前面的都治好了的情况下,总共的最小死亡人数
枚举j,考虑从j+1出发到i,然后回头到j+1,再回头到i,路途共消耗3∗(i−(j+1))天,治疗消耗(i−j+1)天,共4∗(i−j)−2天,每天死亡sum[i+1,n]的人
f[i]=min(f[j]+g[i][j+1]+sum[i+1,n]∗((i−j)∗4−2))
我的收获:真–脑洞……辅助dp……挺常用的……
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define ll long long
const int M=3005;
int n,a[M];
ll s[M],g[M][M],f[M];
int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
inline ll sum(int l,int r){return s[r]-s[l-1];}
void pre()
{
for(int i=1;i<=n;i++){
g[i][i]=0;
for(int j=i-1;j>=1;j--)
g[i][j]=g[i][j+1]+sum(j+1,i)+min(3ll*(i-j)*a[j],sum(j+1,i));
}
}
void work()
{
memset(f,0x3f,sizeof(f));f[0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++) f[i]=min(f[i],f[j]+g[i][j+1]+sum(i+1,n)*(4ll*(i-j)-2));
printf("%lld\n",f[n]);
}
void init()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read(),s[i]=s[i-1]+a[i];
pre();
}
int main()
{
init();
work();
return 0;
}