P3786 萃香抱西瓜 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)(题面太长了,复制过来很麻烦)
他看上去像一个状压dp,而且t,h,w,m的范围都很小,就直接设计一个dp[i][x][y][f]表示在第i秒,角色位置在(x,y),目前抱西瓜的状态为f时最少要走的步数。
然后可以发现t只能向t+1转移,符合DAG的性质,可以放心打dp,但是每次遍历还需要判断能不能走,似乎很麻烦(?
(突然发现可以预处理每个时间点的每个位置能不能到达)
定义一个数组can[t][x][y]表示t时间时(x,y)能不能到达,还有一个flag[t][x][y]数组表示在t时间时在(x,y)的西瓜编号
(注意转移的时候不要漏判可以原地不动的情况,还有dp的初始化,如果一开始在出发点有小西瓜是要初始化进去的)
(dp的转移方程在代码里看吧)
可能全分有点困难,下面还有部分分的得分方法(如果我能看出来的话之后的博客都会有):
子任务 1:所有西瓜静止,且不需要捡任何一个
做法:直接判断初始位置有没有大西瓜即可
子任务 2~3:所有西瓜静止,需要捡小于10个西瓜
做法:因为需要捡的数量最多只有10个,而且不动,可以直接状压跑最短路或者dp求解
子任务4~5:西瓜会运动,不需要捡西瓜
做法:按时间将图分层,然后跑最短路或者dp
子任务1~10:(最开始)
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; long long h,w,t,sx,sy,n,m; long long dp[105][6][6][1<<10+1]; inline long long read(){ long long s=0,f=1; char c=getchar(); while(!isdigit(c)){ if(c=='-'){ f=-f; } c=getchar(); } while(isdigit(c)){ s=(s<<3)+(s<<1)+(c^48); c=getchar(); } return s*f; } long long can[105][6][6],flag[105][6][6]; int main(){ memset(dp,0x3f,sizeof(dp)); h=read(); w=read(); t=read(); sx=read(); sy=read(); for(int i=1;i<=t;i++){ for(int j=1;j<=h;j++){ for(int k=1;k<=w;k++){ can[i][j][k]=1; } } } n=read(); m=read(); long long wz=0,tm1=0,tm2=0,x,y,ok=0; for(int i=1;i<=n;i++){ tm1=read(); tm2=read(); ok=read(); if(ok==1){ wz++; for(int j=tm1;j<tm2;j++){ x=read(); y=read(); flag[j][x][y]=wz; } }else { for(int j=tm1;j<tm2;j++){ x=read(); y=read(); can[j][x][y]=0; } } } if(can[1][sx][sy]==0){ cout<<-1<<endl; } if(flag[1][sx][sy]!=0){ dp[1][sx][sy][1<<(flag[1][sx][sy]-1)]=0; }else { dp[1][sx][sy][0]=0; } for(int i=1;i<=t;i++){ for(int j=1;j<=h;j++){ for(int k=1;k<=w;k++){ for(int now=0;now<(1<<m);now++){ if(dp[i][j][k][now]==4557430888798830399){ continue; } if(can[i+1][j-1][k]){ if(flag[i+1][j-1][k]){ dp[i+1][j-1][k][now|(1<<(flag[i+1][j-1][k]-1))]=min(dp[i+1][j-1][k][now|(1<<(flag[i+1][j-1][k]-1))],dp[i][j][k][now]+1); }else{ dp[i+1][j-1][k][now]=min(dp[i+1][j-1][k][now],dp[i][j][k][now]+1); } } if(can[i+1][j][k-1]){ if(flag[i+1][j][k-1]){ dp[i+1][j][k-1][now|(1<<(flag[i+1][j][k-1]-1))]=min(dp[i+1][j][k-1][now|(1<<(flag[i+1][j][k-1]-1))],dp[i][j][k][now]+1); }else{ dp[i+1][j][k-1][now]=min(dp[i+1][j][k-1][now],dp[i][j][k][now]+1); } } if(can[i+1][j+1][k]){ if(flag[i+1][j+1][k]){ dp[i+1][j+1][k][now|(1<<(flag[i+1][j+1][k]-1))]=min(dp[i+1][j+1][k][now|(1<<(flag[i+1][j+1][k]-1))],dp[i][j][k][now]+1); }else{ dp[i+1][j+1][k][now]=min(dp[i+1][j+1][k][now],dp[i][j][k][now]+1); } } if(can[i+1][j][k+1]){ if(flag[i+1][j][k+1]){ dp[i+1][j][k+1][now|(1<<(flag[i+1][j][k+1]-1))]=min(dp[i+1][j][k+1][now|(1<<(flag[i+1][j][k+1]-1))],dp[i][j][k][now]+1); }else{ dp[i+1][j][k+1][now]=min(dp[i+1][j][k+1][now],dp[i][j][k][now]+1); } } if(can[i+1][j][k]){ if(flag[i+1][j][k]){ dp[i+1][j][k][now|(1<<(flag[i+1][j][k]-1))]=min(dp[i+1][j][k][now|(1<<(flag[i+1][j][k]-1))],dp[i][j][k][now]); }else{ dp[i+1][j][k][now]=min(dp[i+1][j][k][now],dp[i][j][k][now]); } } } } } } long long fk=0,ans=999999999; for(int i=1;i<=h;i++){ for(int j=1;j<=w;j++){ if(dp[t][i][j][(1<<m)-1]==4557430888798830399){ continue; } ans=min(ans,dp[t][i][j][(1<<m)-1]); fk=1; } } if(fk==0){ cout<<-1<<endl; }else{ cout<<ans<<endl; } return 0; }
(谢谢观看!)