题解 P1070 【道路游戏】
ButterflyDew · · 题解
写这个题真是累啊,好毒。应该练练处理比较麻烦的DP。
在读完题目以后,其实初始版的方程并不难想
确实很不完美,我当时也只是想到了这么多,感觉过个90还是比较轻松的,1000的点拿单调队列优化一下就行了。
然后我就开始写
这个题把点权释放到了边权上,人话就是存储的路径位置其实是某个点伸出去的那一条
比如用这个图来描述读入的某时间某费用数组
好吧,到这里我已经感觉我写不出单调队列了。
交了一下果然拿到了90分,其实在如果在考场上做到这里已经可以了(鬼知道为什么部分分有这么多)
部分分代码:
#include <cstdio>
#include <cstring>
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
const int N=1010;
const int inf=0x3f3f3f3f;
int dp[N][N][2],n,m,p,harv[N][N],f[N][N],cost[N],ans=-inf;//n数量,m时间
int cal(int i,int j,int k)
{
return f[i-1][j-1]-f[i-k-1][j-k-1];
}
int main()
{
memset(dp,-0x3f,sizeof(dp));
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&harv[i][j]);
f[j][i]=f[j-1][i-1]+harv[i][j];
}
for(int i=1;i<=n;i++)
scanf("%d",cost+i);
for(int i=1;i<=n;i++)
dp[1][i][0]=-cost[i];
m++;
for(int i=2;i<=m;i++)//时间
{
for(int j=1;j<=n;j++)//路程
{
for(int k=1;k<=min(i,p);k++)//从第几个之前转移
{
if(j>k)
dp[i][j][1]=max(dp[i][j][1],dp[i-k][j-k][0]+cal(i,j,k));
else if(i>j)
dp[i][j][1]=max(dp[i][j][1],dp[i-k][n+j-k][0]+cal(i-j+1,n+1,k-j+1)+cal(i,j,j-1));
}
}
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
if(k==j) continue;
dp[i][j][0]=max(dp[i][j][0],dp[i][k][1]-cost[j]);
}
}
for(int i=1;i<=n;i++)
ans=max(ans,dp[m][i][1]);
printf("%d\n",ans);
return 0;
}
看看各位佬爷的题解。
才发现自己的方程太不优秀了,优秀的方程
转移时维护
因为每一个
对同一个答案的贡献,这个分布大概是这样。
为了准确的定位某个答案从哪个单调队列转移,考虑给每一个单调队列编号,将单队与
在还没拐弯时,所属单队即为
int get(int i,int j)//获取单队编号
{
return ((j-i)%n+n)%n;
}
还有两点要注意的地方
一是虚线所连的边仍然需要特判一下
二是为了确保拐弯后不出现问题,要把
参考代码:
#include <cstdio>
#include <cstring>
const int N=1010;
int max(int x,int y){return x>y?x:y;}
int n,m,p;
int f[N][N],cost[N],q[N][N],loc[N][N],l[N],r[N],add[N],dp[N];
int get(int i,int j)//获取单队编号
{
return ((j-i)%n+n)%n;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&f[j][i]);
f[j][i]+=f[j-1][i-1];
}
for(int i=0;i<n;i++)
{
scanf("%d",cost+i);
q[i][++r[i]]=-cost[i],l[i]++;
}
memset(dp,-0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=m;i++)
{
for(int j=0;j<n;j++)
{
int id=get(i,j);
while(l[id]<=r[id]&&loc[id][l[id]]+p<i) l[id]++;
if(!j) add[id]+=f[i][n];
if(l[id]<=r[id])
dp[i]=max(dp[i],q[id][l[id]]+add[id]+f[i][j]);
}
for(int j=0;j<n;j++)
{
int id=get(i,j);
int tmp=dp[i]-add[id]-f[i][j]-cost[j];
while(l[id]<=r[id]&&q[id][r[id]]<=tmp)
r[id]--;
loc[id][++r[id]]=i;
q[id][r[id]]=tmp;
}
}
printf("%d\n",dp[m]);
return 0;
}