题解 P4772 【灰化肥,会挥发】

P4772 灰化肥,会挥发

题目大意:

给出一矩阵,求经过所有仓库的最短路。

solution:

看到很小的 \(n\) 的范围,我们考虑状压。跟 P4802 [CCO 2015]路短最 不同的是,本题给出了矩阵,所以我们考虑如何建图:

我们从每个点出发,进行 \(\text{BFS}\) ,这样就可以愉快的状压了。

状态 \(f[\,i\,][\,j\,]\) 为当前状态为 \(\,i\,\) 走到了 \(\,j\,\) 点的最短路径长,\(g[\,i\,][\,j\,]\) 为当前状态为 \(\,i\,\) 走到了 \(\,j\,\) 点的最小字典序路径。

至于路径长,易得状态转移方程为:

\[f[\,i\,][\,j\,]= \text{max}(f[\,i\,][\,j\,],f[\,i\, \bigoplus\,(1\ll j)][\,k\,]+dis[\,k\,][\,j\,]) \]

思考一下最小字典序路径,这里 \(g[\,i\,][\,j\,]\) 我用的是 \(\text{string}\) 类型,方便字符串的拼接与比较。转移我们考虑两种情况:

  1. 当前最短路更新了,相应的路径也要更新。
  2. 当前最短路无法更新,但与原状态相等,并且当前的路径字典序较小,可更新。

细节处理:

  • 只能从 \(\text{A}\) 点出发。
  • 最后统计答案要赋个极大值。

看到这的同学,可以自己去写代码了(tf口吻)

代码
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int R=505,N=17;
int r,c,n;
char mp[R][R];
struct D{int x,y;}d[N];
inline D make_D(int i,int j){
	D now; now.x=i,now.y=j;
	return now;
}
int l[R][R]; bool vis[R][R];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
inline bool intu(int x,int y){
	if(x<1||x>r||y<1||y>c) return 0;
	return 1;
}
inline void bfs(int s){
	memset(l,0,sizeof(l));
	memset(vis,0,sizeof(vis));
	vis[d[s].x][d[s].y]=1;
	queue<D> q; q.push(d[s]);
	while(q.size()){
		int x=q.front().x,y=q.front().y; 
		q.pop();
		for(int i=0;i<4;i++){
			int zx=x+dx[i],zy=y+dy[i];
			if(!intu(zx,zy)||vis[zx][zy]||mp[zx][zy]=='*') 
				continue;
			l[zx][zy]=l[x][y]+1,vis[zx][zy]=1;
			q.push(make_D(zx,zy));
		}
	}
}
int f[1<<N][N],dis[N][N];
string g[1<<N][N];
int main(){
	scanf("%d%d%d",&r,&c,&n);
	for(int i=1;i<=r;i++)
		scanf("%s",mp[i]+1);
	for(int i=1;i<=r;i++)
		for(int j=1;j<=c;j++)
			if('A'<=mp[i][j]&&mp[i][j]<='Z')
				d[mp[i][j]-'A']=make_D(i,j);
	memset(dis,0x3f,sizeof(dis));
	for(int i=0;i<n;i++){
		bfs(i);
		for(int j=0;j<n;j++)
			dis[i][j]=l[d[j].x][d[j].y];
	}
	memset(f,0x3f,sizeof(f));
	f[1][0]=0,g[1][0]="A";
	for(int i=1;i<(1<<n);i++)
		for(int j=0;j<n;j++)
			if(i>>j&1)
				for(int k=0;k<n;k++)
					if(i>>k&1&&k!=j)
						if(f[i][j]>f[i^(1<<j)][k]+dis[k][j])
							f[i][j]=f[i^(1<<j)][k]+dis[k][j],
							g[i][j]=g[i^(1<<j)][k]+(char)(j+'A');
						else if(f[i][j]==f[i^(1<<j)][k]+dis[k][j]&&g[i][j]>g[i^(1<<j)][k])
							g[i][j]=g[i^(1<<j)][k]+(char)(j+'A');
	int ans1=0x7fffffff; 
	string ans2="ZZZZZZZZZZZZZZZZZZ";
	for(int i=1;i<n;i++){
		if(ans1>f[(1<<n)-1][i])
			ans1=f[(1<<n)-1][i],
			ans2=g[(1<<n)-1][i];
		else if(ans1==f[(1<<n)-1][i]&&ans2>g[(1<<n)-1][i])
			ans2=g[(1<<n)-1][i];
	}
	printf("%d\n",ans1);
	cout<<ans2;
	return 0;
}

End

posted @ 2021-07-29 19:48  Mr_think  阅读(678)  评论(0)    收藏  举报