洛谷 P3786 萃香抱西瓜

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;
}

(谢谢观看!)

原文地址:https://www.cnblogs.com/lichangjian/p/15123810.html