最短路练习题题解

小木乃伊到我家

/**
 * 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;
const int mod=1e9+7;

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

int n,m;

int d[maxn];
bool vis[maxn];

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

bool 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()){
		pii p=que.top();que.pop();
		if(vis[p.se])continue;
		int i=p.se,cost1=p.fi;
		vis[i]=1;
		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(cost2<d[id]){
				d[id]=cost2;
				que.push({cost2,id});
			}
		}
	}
	return vis[n];
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	cout<<(dijkstra()?std::to_string(d[n]):"qwb baka")<<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;
}

/**
 * 简单题
 * 题目中说了给定一个n-1条边的所有点都联通的图,本质上就是一棵树
 * 那么问题就变成了给定一棵以p点为根的树,在p点到其他点的距离中,求第k小的值
 * 既然是一棵树,那么树上任意两点的简单路径有且仅有一条,其距离是确定的
 * 那么直接从p点开始bfs或者dfs整棵树求出d数组即可,代码给出了bfs和dfs的做法
 */

#pragma GCC optimize(3)
#include<bits/stdc++.h>

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

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

const int maxn=1e6+5;
const int maxm=2e6+5;
const int mod=1e9+7;

int head[maxn],to[maxm],ne[maxm],w[maxn],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;
}

int n,p,k;
int d[maxn];
bool vis[maxn];
void bfs(int s){
	std::queue<int> que;
	que.push(s);
	vis[s]=true,d[s]=0;
	while(!que.empty()){
		int i=que.front();
		que.pop();
		for(int e=head[i],id=to[e];e;e=ne[e],id=to[e]){
			if(vis[id])continue;
			vis[id]=true;
			d[id]=d[i]+w[e];
			que.push(id);
		}
	}
}

void dfs(int i){
	vis[i]=true;
	for(int e=head[i],id=to[e];e;e=ne[e],id=to[e]){
		if(vis[id])continue;
		d[id]=d[i]+w[e];
		dfs(id);
	}
}
void solve(){
	cin>>n>>p>>k;
	for(int i=1;i<n;i++){
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	//bfs(p);
	dfs(p);
	k++;
	//将d数组中第k小的值放在下标为k的位置
	std::nth_element(d+1,d+k,d+1+n);
	cout<<d[k]<<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简单题
 * 题目:给定一个无向图,求出(任意不同三点)x->y->z在走最短路线情况下的最大距离
 * 遍历中间点y,对每个中间点跑dijkstra算法求y点到其他点的最短距离,
 * 取距离中不为0且可以达到的最大的两个距离则为以y为中间点的最优解
 * 答案即为所有中间点的最优解中的最大值
 */
#pragma GCC optimize(3)
#include<bits/stdc++.h>

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

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

const int maxn=1e3+5;
const int maxm=2e3+5;
const int mod=1e9+7;

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

int n,m;

int d[maxn];
bool vis[maxn];

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

void dijkstra(int s){
	//初始化数据
	for(int i=1;i<=n;i++)d[i]=1e18,vis[i]=false;
	d[s]=0;
	std::priority_queue<pii,std::vector<pii>,std::greater<pii>> que;
	que.push({0,s});
	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});
			}
		}
	}
}
void solve(){
	cin>>n>>m;
	
	//多组样例输入,每次都必须初始化图
	id=0;
	for(int i=1;i<=n;i++)head[i]=0;
	
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	int ans=-1;
	for(int i=1;i<=n;i++){
		dijkstra(i);
		std::sort(d+1,d+1+n);
		int ed=n;
		//距离为1e18的点为不可到达的点
		while(d[ed]==1e18)ed--;
		//d[1]必然等于0,因为i到i的距离为0.
		//如果ed>=3,则代表至少有其他的两个点可以从i点到达,取最大的两个距离,即d[ed-1]和d[ed]
		if(ed>=3)ans=std::max(ans,d[ed-1]+d[ed]);
	}
	cout<<ans<<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;
}

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;//每个点在入队之后,其vis值标记为true,表示已经遍历过
	d[0]=0;//0->0的距离为0
	last[0]=-1;//0->0的路径中,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;
	//从终点n+1,从后往前把最短路径上的点一个一个放进path数组
	//如果now==-1,则表示上一轮循环已经把起始点0点放进path数组,故可结束循环
	while(now!=-1){
		path.push_back(now);
		now=last[now];
	}
	cout<<d[n+1]-1<<endl;
	//从起点到终点,逆向打印path中的数组值
	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;
}

Operating System

/**
 * 贪心:每次需要页面置换时,替换出内存里面下次使用时刻最远的页面
 * 使用set模拟内存,记录内存里每一个页面的编号以及下一次使用的时刻
 * 运算符重载不仅适用于结构体类型的优先队列,对结构体类型的各种set,map都是适用的!
 */
//#pragma GCC optimize(3)
#include<bits/stdc++.h>

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

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

const int maxn=5e4+5;
int n,m,q;
int a[maxn];
int b[maxn];//b是辅助初始化ne数组的,b[a[i]]记录的是a[i]这个数当前时刻出现的位置,详见代码
int ne[maxn];//ne[i]表示a[i]下一次出现的下标位置

struct T{
	int id,next;//编号,下次使用时刻
	
	//注意运算符重载next值越大,结果越小!!!,表示按照next降序排列
	bool operator <(const T &t)const{
		return next>t.next;
	}
};

void solve(){
	//多组数据初始化
	for(int i=1;i<=m;i++)b[i]=0;
	
	for(int i=1;i<=q;i++)cin>>a[i];
	
	//倒叙遍历!!!
	for(int i=q;i>=1;i--){
		//先更新ne数组,后更新b数组,顺序不能乱
		//此时b[a[i]]记录的是上一次记录的a[i]的下标,b[a[i]]不为0则表示在此之前有出现过a[i]这个数
		//b[a[i]]为0则代表没有下一次使用,故此时下次使用时刻ne[i]记为最大值(q+1),是最理想的替换页面
		ne[i]=(b[a[i]]?b[a[i]]:q+1);
		b[a[i]]=i;
	}
	int ans=0;
	std::set<T> set;
	for(int i=1;i<=q;i++){
		if(set.count({a[i],i})){
			
			//如果set内存里已经有a[i](之前放进内存的),则对应的值一定是{a[i],i}
			//因为上一个值为a[i]的下标(假设为j,)放的是{a[j],ne[j]},而a[j]==a[i],ne[j]=i
			
			
			//改变下一次出现的位置i为ne[i]
			set.erase({a[i],i});
			set.insert({a[i],ne[i]});
		}else{
			//set内存里没有a[i],需要添加a[i]号页面,先判断内存是否满
			if((int)set.size()==n)set.erase(set.begin());//内存满了,删除下次使用时刻最远的内存页面
			set.insert({a[i],ne[i]});//添加页面
			ans++;//统计答案
		}
	}
	cout<<ans<<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);
	
	while(cin>>n>>m>>q){
		solve();
	}
	
	return 0;
}

阿宁去游玩

/*
  此题难就难在,随着膜法的使用,(花费)边的权值可能会有所不同,
  但是实际上,在最优情况下,边的权值是固定的!!!

  如果当前选中的是i号点,那么在最优情况下,
  i点的所有vis值为false的邻点id,其与i点的属性异同性不变,
  从起点走到i点的过程中,若经过id点,则vis[id]为true,矛盾,
  故从起点走到i点,不会经过id点,
  所以从起点走到i点的途中,若使用了膜法,必然i和id两者会同时变化,因此两者异同性不变
  

  "关键在于i点"
  如果在i点没使用膜法,则两者异同性不变,花费为cost1=((a[i]^a[j])y:x)
  如果在i点使用了膜法,则两者异同性会改变,花费为cost2=z+((a[i]^a[j])?x:y)
  最优情况下应该取 min(cost1,cost2),此即为路径长度(边的权值)
  
  因此,对任意的i点都是如此,即对所有的点都满足该条件,所以在最优情况下,边的权值是固定的!
  
  建完图之后直接复制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 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;
}

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

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

int dijkstra(){
	for(int i=1;i<=n;i++)vis[i]=false,d[i]=1e18;
	d[1]=0;
	std::priority_queue<pii,std::vector<pii>,std::greater<pii>> que;
	que.push({d[1],1});
	while(!que.empty()){
		pii p=que.top();
		que.pop();
		int i=p.se;
		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]&&d[id]>d[i]+w[e]){
				d[id]=d[i]+w[e];
				que.push({d[id],id});
			}
		}
	}
	return d[n];
}

void solve(){
	cin>>n>>m>>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;
		int cost1=((a[u]^a[v])?y:x);
		int cost2=z+((a[u]^a[v])?x:y);
		add(u,v,std::min(cost1,cost2));
	}
	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、付费专栏及课程。

余额充值