最短路题解

Word:

/**
 * 令字符串s为s[0],字符串t为s[n+1],其他字符串为s[1-n],每个字符串都对应一个编号
 * 双重for循环遍历所有字符串对,若满足转换条件,则对应的编号连接一条权值为1的边,
 * 显然满足转换条件当且仅当两个字符串有且仅有一个字符不同.
 * 
 * 显然从s[0]到s[n+1]的最小操作数可以转换为从点0到点n+1的最短路径长度
 * 
 * 因此连完边之后直接跑最短路算法,由于边权都为1,故可直接使用bfs跑最短路,
 * 同时用last数组记录路径,
 * last[i]表示从s[last[i]]转换到s[i]是从s[0]到s[i]最短路上的最后一步.
 * 详见代码.
 */
#pragma GCC optimize(3)
#include<bits/stdc++.h>

#define int long long
#define endl '\n'

using std::cin;
using std::cout;

const int maxn=2e3+5;
const int maxm=4e6+5;

int head[maxn],to[maxm],ne[maxm],id;
void add(int x,int y){
	ne[++id]=head[x],head[x]=id,to[id]=y;
	ne[++id]=head[y],head[y]=id,to[id]=x;
}

int n,m;
std::string s[maxn];

bool canAddEdge(int i,int j){
	int cnt=0;
	for(int k=0;k<m;k++){
		cnt+=s[i][k]!=s[j][k];
	}
	return cnt==1;
}

int d[maxn];//从0到i的最短路径
int last[maxn];//从0到i的最短路径上,最后一个是从last[i]到达i
bool vis[maxn];//从0是否可以到达i

void bfs(){
	std::queue<int> que;
	que.push(0);
	vis[0]=true,d[0]=0,last[0]=-1;
	while(!que.empty()){
		int i=que.front();
		que.pop();
		for(int e=head[i],id=to[e];e;e=ne[e],id=to[e]){
			//id之前已经可以被其他点遍历了,
			//根据bfs的特性,此处再次到达的路径和先前的路径长度相等或者更大,不优,因此直接continue
			if(vis[id])continue;
			
			//id先前还没遍历过,此处是第一次遍历到,必定是最短的距离
			que.push(id);
			//更新对应的数组值
			d[id]=d[i]+1,last[id]=i,vis[id]=true;
		}
	}
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>s[i];
	cin>>s[0]>>s[n+1];
	for(int i=0;i<=n+1;i++){
		for(int j=i+1;j<=n+1;j++){
			if(canAddEdge(i,j)){
				add(i,j);
			}
		}
	}
	//特判掉s==t的情况
	if(s[0]==s[n+1]){
		cout<<0<<endl;
		cout<<s[0]<<endl;
		cout<<s[n+1]<<endl;
		return;
	}
	bfs();
	if(!vis[n+1]){
		cout<<-1<<endl;
		return;
	}
	int now=n+1;
	std::vector<int> path;
	while(now!=-1){
		path.push_back(now);
		now=last[now];
	}
	cout<<d[n+1]-1<<endl;
	for(auto it=path.rbegin();it!=path.rend();it++){
		cout<<s[*it]<<endl;
	}
}

signed main(){
	//	freopen("file.txt","r",stdin);
	//	std::ios_base::sync_with_stdio(false);
	//	std::cin.tie(nullptr),std::cout.tie(nullptr);
	//	cout<<std::fixed<<std::setprecision(15);
	
	int tt=1;//cin>>tt;
	while(tt--)solve();
	
	return 0;
}

啊宁去游玩

/**
 * 思维+dijkstra模板
 * 假设从1号点到达点x的最短距离为d[x],此时到达点u(d[u]已知),邻点为v(d[v]未知),
 * 则在最小花费的情况下, 此时"u和v的属性关系一定和初始关系一样",
 * 因为从1->u的最短路径路径必然没有经过v(如果经过v, 则d[v]已知),
 * 因此从1->u的最短路径中,无论使用多少次膜法,
 * u和v的属性都会同时改变所以当恰好到达u点时,故u和v的属性关系一定和初始关系一样.
 * 
 * 
 * 初始关系一样则意味这从u->v的花费是确定
 * 所以从u->v有两种选择:使用与不使用膜法
 * 	   根据u和v的属性关系,又可分两种情况:
 *         若u和v初始属性相同,则u->v的花费为min(x,z+y);
 * 		     			  否则u->v的花费为min(z+x,y)
 * 
 * 由于d[1]=0(已知),而d[2-n]未知
 * 因此直接建图从1号点开始递推,跑堆优化的dijkstra即可
 */
#pragma GCC optimize(3)
#include<bits/stdc++.h>

#define int long long
#define endl '\n'

using std::cin;
using std::cout;

const int maxn=2e5+5;
const int maxm=4e5+5;

int n,m,x,y,z;
bool a[maxn];

int head[maxn],to[maxm],ne[maxm],w[maxm],id;
void add(int x,int y,int z){
	ne[++id]=head[x],head[x]=id,to[id]=y,w[id]=z;
	ne[++id]=head[y],head[y]=id,to[id]=x,w[id]=z;
}

#define fi first
#define se second
#define pii std::pair<int,int>

int d[maxn];
bool vis[maxn];
int dijkstra(){
	for(int i=2;i<=n;i++)d[i]=1e18;
	std::priority_queue<pii,std::vector<pii>,std::greater<pii>> que;
	que.push({0,1});
	while(!que.empty()){
		int i=que.top().se,cost1=que.top().fi;
		que.pop();
		if(vis[i])continue;
		vis[i]=true;
		for(int e=head[i],id=to[e];e;e=ne[e],id=to[e]){
			if(vis[id])continue;
			int cost2=cost1+w[e];
			if(d[id]>cost2){
				d[id]=cost2;
				que.push({d[id],id});
			}
		}
	}
	return d[n];
}

void solve(){
	cin>>n>>m;
	cin>>x>>y>>z;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v,(a[u]==a[v]?std::min(x,y+z):std::min(y,x+z)));
	}
	cout<<dijkstra()<<endl;
}

signed main(){
	//  freopen("file.txt","r",stdin);
	std::ios_base::sync_with_stdio(false);
	std::cin.tie(nullptr),std::cout.tie(nullptr);
	//	cout<<std::fixed<<std::setprecision(15);
	
	int tt=1;//cin>>tt;
	while(tt--)solve();
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值