首页 > 其他分享 >P3070 [USACO13JAN]Island Travels G 题解

P3070 [USACO13JAN]Island Travels G 题解

时间:2023-01-29 10:24:28浏览次数:65  
标签:P3070 连通 ma 题解 ll 路径 Travels dp dis

题目传送门

一道耗费了本蒟蒻与某机房卷王半天的恶心题

题目描述

给定一个图,求每个 X 连通块之间的最短 Hamilton 路径。假如您不知道 Hamilton 路径是什么

分析

这题本质上是 Hamilton 路径的模板题,但是难就难在题目并没有直接将一个抽象过的无向图给你,而是给了一个具体的地图。那么我们第一步就应该将这个地图抽象成一个无向图。

First

首先我们来找连通块。记 \(Belong_{x,y}\) 为坐标为 \((x,y)\) 的 'X' 所属的连通块, \(tot\) 为连通块的总数, \(mp_{x,y}\) 为我们存的地图(将 'S' 存为 \(1\),'X' 存为 \(0\),'.' 存为 -\(1\) )。则我们可以用 DFS 来求连通块 (我是不会说我不会 BFS 的)

ll dx[4]={0,1,0,-1};
ll dy[4]={1,0,-1,0};
ll belong[ma][ma],tot=0;
ll mp[ma][ma];

void dfs1(ll x,ll y){
	if(x>n||y>m||x<1||y<1) return;//到边界停止
	if(mp[x][y]!=0||belong[x][y]) return;//找过了或不是陆地就不找了
	if(mp[x][y]==0){
		belong[x][y]=tot;//记录连通块
	}
	for(ll i=0;i<4;i++){
		ll xx=x+dx[i],yy=y+dy[i];//位移深搜
		dfs1(xx,yy);
	}
}

Second

找完了联通块,就开始建图了。我们记 \(w_{i,j}\) 为联通块 \(i\) 到 \(j\) 的最短路径。那么我们要如何求这个最短路径呢?类比双端队列优化的 SPFA,我们用一个双端队列对每一个连通块都跑一遍 BFS,记 \(dis_{i,j}\) 为当前连通块到 \((i,j)\) 的最短路径,再去用 \(dis_{i,j}\) 的最小值更新 \(w_{i,j}\) 即可。

deque<pair<ll,ll>> q;
ll dis[ma][ma];
ll w[16][16];

void bfs(){
	while(q.size()){
		ll x=q.front().first,y=q.front().second;
		q.pop_front();
		for(ll i=0;i<4;i++){
			ll xx=x+dx[i],yy=y+dy[i];
			if(xx>n||yy>m||xx<1||yy<1) continue;//越界
			if(dis[xx][yy]||mp[xx][yy]==-1) continue;//访问过
			//如果是 'X' ,则推到队首,距离不变
			if(mp[xx][yy]==0){
				dis[xx][yy]=dis[x][y];
				q.push_front(make_pair(xx,yy));
			}
			//如果是 'S' ,则推到队尾,距离加1
			else if(mp[xx][yy]==1){
				dis[xx][yy]=dis[x][y]+1;
				q.push_back(make_pair(xx,yy));
			}
		}
	}
}

//in main()

memset(w,0x3f,sizeof(w));
for(ll i=1;i<=tot;i++){
	memset(dis,0,sizeof(dis));
	for(ll j=1;j<=n;j++){
		for(ll k=1;k<=m;k++){
			if(belong[j][k]==i){
				//如果(j,k)属于第i个连通块,则推到队首
				q.push_front(make_pair(j,k));
				dis[j][k]=1;//小技巧:为了防止0在 BFS 时一直不被更新,初始便设置为1。
			}
		}
	}
	bfs();
	for(ll j=1;j<=n;j++){
		for(ll k=1;k<=m;k++){
			//(j,k)为枚举的点
			for(ll cx=1;cx<=n;cx++){
				for(ll cy=1;cy<=m;cy++){
					//(cx,cy)为属于第i个连通块的点
					if(belong[j][k]&&belong[cx][cy]&&belong[cx][cy]==i){
						w[i][belong[j][k]]=min(w[i][belong[j][k]],dis[j][k]-dis[cx][cy]);//更新w
					}
				}
			}
		}
	}
}

Final

现在,我们总算将这题最最 恶心 难的地方处理完啦,接下来便是纯纯的 最短 Hamilton 路径 板子。但是注意我们是可以随便选起点的,所以要对每一个点作为起点都跑一遍状压 DP,求出最小的答案即可。

状压 DP 具体见这题

ll dp[16][1<<16];

memset(dp,0x3f,sizeof(dp));
for(ll i=1;i<=tot;i++) dp[i][1<<(i-1)]=0;
for(ll i=1;i<=tot;i++){
	for(ll j=1<<(i-1)+1;j<1<<tot;j++){
		for(ll k=1;k<=tot;k++){
			if((j>>(k-1))&1){
				for(ll l=1;l<=tot;l++){
					if((j^1<<(k-1))>>(l-1)&1){
						dp[k][j]=min(dp[k][j],dp[l][j^1<<(k-1)]+w[l][k]);
					}
				}
			}
		}
	}
}
ll ans=inf;
for(ll i=1;i<=tot;i++) ans=min(ans,dp[i][(1<<tot)-1]);

完整代码

#include<bits/stdc++.h>
#define ll long long
#define ma 100
#define mod 1000000007
#define il inline
using namespace std;
const ll inf=1e18+10;
ll read(){
	char ch=getchar();
	ll x=0,f=1;
	while(ch>'9'||ch<'0'){
	if(ch=='-') f=-1;
	ch=getchar();
	}
	while('0'<=ch&&ch<='9'){
	x=x*10+ch-'0';
	ch=getchar();
	}
	return x*f;
}
ll n,m;
ll mp[ma][ma];
ll dx[4]={0,1,0,-1};
ll dy[4]={1,0,-1,0};
ll dp[16][1<<16];
ll w[16][16];
ll belong[ma][ma],tot=0;
deque<pair<ll,ll>> q;
ll dis[ma][ma];
void dfs1(ll x,ll y){
	if(x>n||y>m||x<1||y<1) return;
	if(mp[x][y]!=0||belong[x][y]) return;
	if(mp[x][y]==0){
		belong[x][y]=tot;
	}
	for(ll i=0;i<4;i++){
		ll xx=x+dx[i],yy=y+dy[i];
		dfs1(xx,yy);
	}
}
void bfs(){
	while(q.size()){
		ll x=q.front().first,y=q.front().second;
		q.pop_front();
		for(ll i=0;i<4;i++){
			ll xx=x+dx[i],yy=y+dy[i];
			if(xx>n||yy>m||xx<1||yy<1) continue;
			if(dis[xx][yy]||mp[xx][yy]==-1) continue;
			if(mp[xx][yy]==0){
				dis[xx][yy]=dis[x][y];
				q.push_front(make_pair(xx,yy));
			}
			else if(mp[xx][yy]==1){
				dis[xx][yy]=dis[x][y]+1;
				q.push_back(make_pair(xx,yy));
			}
		}
	}
}
int main(){
	n=read(),m=read();
	for(ll i=1;i<=n;i++){
		char x[100];
		cin>>x+1;
		for(ll j=1;j<=m;j++){
			if(x[j]=='.') mp[i][j]=-1;
			if(x[j]=='X') mp[i][j]=0;
			if(x[j]=='S') mp[i][j]=1;
		}
	}
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=m;j++){
			if(!belong[i][j]&&!mp[i][j]){
				++tot;
				dfs1(i,j);
			}
		}
	}
	memset(w,0x3f,sizeof(w));
	for(ll i=1;i<=tot;i++){
		memset(dis,0,sizeof(dis));
		for(ll j=1;j<=n;j++){
			for(ll k=1;k<=m;k++){
				if(belong[j][k]==i){
					q.push_front(make_pair(j,k));
					dis[j][k]=1;
				}
			}
		}
		bfs();
		for(ll j=1;j<=n;j++){
			for(ll k=1;k<=m;k++){
				for(ll cx=1;cx<=n;cx++){
					for(ll cy=1;cy<=m;cy++){
						if(belong[j][k]&&belong[cx][cy]&&belong[cx][cy]==i){
							w[i][belong[j][k]]=min(w[i][belong[j][k]],dis[j][k]-dis[cx][cy]);
						}
					}
				}
			}
		}
	}
	memset(dp,0x3f,sizeof(dp));
	for(ll i=1;i<=tot;i++) dp[i][1<<(i-1)]=0;
	for(ll i=1;i<=tot;i++){
		for(ll j=1<<(i-1)+1;j<1<<tot;j++){
			for(ll k=1;k<=tot;k++){
				if((j>>(k-1))&1){
					for(ll l=1;l<=tot;l++){
						if((j^1<<(k-1))>>(l-1)&1){
							dp[k][j]=min(dp[k][j],dp[l][j^1<<(k-1)]+w[l][k]);
						}
					}
				}
			}
		}
	}
	ll ans=inf;
	for(ll i=1;i<=tot;i++) ans=min(ans,dp[i][(1<<tot)-1]);
	printf("%lld\n",ans);
	return 0;
}

部分思想参考了卷王cry

标签:P3070,连通,ma,题解,ll,路径,Travels,dp,dis
From: https://www.cnblogs.com/mukar/p/17071899.html

相关文章

  • 【题解】P4491 [HAOI2018]染色
    思路NTT优化二项式反演。首先考虑到求“正好有\(k\)种颜色出现\(S\)次”的方案数,所以可以考虑转化成求“至少有\(k\)种颜色出现\(S\)次”的方案数。形式化......
  • ABC 287 (E-Ex) 题解
    E我的做法对于每个串枚举他的答案,然后直接hash硬干就完了。卡一卡就过去了#include<bits/stdc++.h>usingnamespacestd;typedefunsignedlonglongull;const......
  • setting.xml的mirror、mirrorOf和pom.xml的repositories、repository的关系关联snapsh
    setting.xml的mirror、mirrorOf和pom.xml的repositories、repository的关系关联snapshots带有时间错问题解决方案nexus3.8私有仓库https://blog.csdn.net/Michaelwubo/a......
  • [CF380C] Sereja and Brackets 题解
    [CF380C]SerejaandBrackets题解给定一个括号串\(s\)与\(m\)次询问。l,r回答字符串\(t=s_ls_{l+1}\dotss_r\)​的所有子序列中最长的合法括号串的长度。......
  • 【题解】[ABC286C] Rotate and Palindrome 题解
    更好的阅读体验洛谷题目传送门|AT原题传送门思路观察题目可以发现A操作最多只能执行\(n\)次,超过以后字符串又会回到初始状态。首先考虑A操作如何实现,一种办法......
  • 【题解】[IOI2018] werewolf 狼人
    题目分析这个题本身很简单,可能就是因为是IOI题所以看上去就难了些吧。其实题目就是让我们先走一段全部大于等于\(l\)的点然后再走一段全部小于等于\(r\)的点,那么能......
  • xhost: unable to open display ""问题解决
    一、需求xhost可是增强vnc的图形界面显示二、问题解决设置一下dispaly  通过执行exportDISPLAY=x.x.x.x:1,x.x.x.x指的是虚拟机ip地址,DISPLAY用来设置将图形显示......
  • 视频美颜sdk疑难问题解答
    目前,美颜sdk已经成了大多数直播、视频平台的刚需,因为这是用户需求,同样也是市场的风向。随着需求量的暴增,视频美颜sdk一些技术向的提问也多了起来,今天小编就为大家讲解一下视......
  • # CF#847 (Div. 3)ABCDE题解
    CodeforcesRound#847(DFiv.3)APolycarpandtheDayofPiProblem-A-Codeforces题目描述OnMarch14,thedayofthenumber$\pi$iscelebratedallov......
  • 【题解】洛谷-P5643 [PKUWC2018]随机游走
    用到了概率期望的很多技巧。首先要求的是走遍集合中所有节点步数的期望,也就是步数最大值的期望,根据\(\text{min-max}\)容斥,有:\[E\left(\max_{i\inS}x_i\right)=\sum_......