图论初体验

Hide and Seek 捉迷藏

Description

贝茜在和约翰玩一个“捉迷藏”的游戏.

她正要找出所有适合她躲藏的安全牛棚.一共有N(2≤N≤20000)个牛棚,被编为1到N号.

她知道约翰(捉牛者)从牛棚1出发.所有的牛棚由M(1≤M≤50000)条双向路连接,每条双向路连接两个不同的牛棚.

所有的牛棚都是相通的.贝茜认为同牛棚1距离最远的的牛棚是安全的.

两个牛棚间的距离是指,从一个牛棚到另一个牛棚最少需要通过的道路数量.请帮贝茜找出所有的安全牛棚.

Format

Input

第1行输入两个整数N和M,

之后M行每行输入两个整数,表示一条路的两个端点

Output

仅一行,输出三个整数.第1个表示安全牛棚(如果有多个,输出编号最小的);第2个表示牛棚1和安全牛棚的距离;第3个表示有多少个安全的牛棚

Samples

输入数据 1

6 7
3 6
4 3
3 2
1 3
1 2
2 4
5 2

输出数据 1

4 2 3

单元最短路——dijkstra板子 

dijkstra算法的精髓在于每个队列头都是对于当前点的最优解,进而以当前点为中转点,更新与其直接相连的点。 

#include<bits/stdc++.h>
using namespace std;
int n,m,mx,ans,d[20003],to;
bool vis[20003],fg=false;
vector<int>f[20003];
struct node
{
	int u,dist;
};
bool operator<(node a,node b)
{
	return a.dist>b.dist;
}
priority_queue<node> q;
node temp;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&mx,&ans);
		f[mx].push_back(ans);
		f[ans].push_back(mx);
	}
	memset(vis,false,sizeof(vis));
	memset(d,127/2,sizeof(d));
	d[1]=0;
	q.push({1,0});
	while(q.empty()==false)
	{
		temp=q.top();
		q.pop();
		if(vis[temp.u]==true)
			continue;
		vis[temp.u]=true;
		for(int i=0;i<f[temp.u].size();i++)
		{
			to=f[temp.u][i];
			if(d[to]>d[temp.u]+1)
			{
				d[to]=d[temp.u]+1; 
				q.push({to,d[to]}); 
			}
		}	
	}
	mx=0;
	ans=0;
	for(int i=1;i<=n;i++)
		mx=max(mx,d[i]);
	for(int i=1;i<=n;i++)
		if(d[i]==mx)
		{
			if(fg==false)
			{
				fg=true;
				printf("%d ",i);
			}
			ans++;
		}
	printf("%d %d",mx,ans);	
	return 0;
}

 

[JLOI2011]飞行路线

Description

Alice 和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这 些城市分别标记为0到n-1,一共有m种航线,每 种航线连接两个城市,并且航线有一定的价格。

Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推 出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?

Input

数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。

第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。(0<=s,t<n)

接下来有m行,每行三个整数,a,b,c,表示存在一种航线,能从城市a到达城市b,或从城市b到达城市a,价格为c。

(0<=a,b<n,a与b不相等,0<=c<=1000) 2<=n<=10000,1<=m<=50000,0<=k<=10.

Output

只有一行,包含一个整数,为最少花费。

Samples

输入数据 1

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

输出数据 1

8

最短路+dp

dp的状态为使用免费的次数 

#include<bits/stdc++.h>
using namespace std;
int n,m,k,s,t,x,y,z,u,v,fr,to;
bool vis[10003][12];
struct node
{
	int u,v,fr;
};
vector<node>mp[10003];
priority_queue<node> q;
bool operator<(node a,node b)
{
	return a.v>b.v;
}
node temp;
int main()
{
	scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		mp[x].push_back({y,z,0});
		mp[y].push_back({x,z,0}); 
	}	
	memset(vis,false,sizeof(vis));
	q.push({s,0,0});
	while(q.empty()!=true)
	{
		temp=q.top();
		q.pop();
		u=temp.u;
		v=temp.v;
		fr=temp.fr;
		if(u==t)
		{
			printf("%d",v);
			return 0;
		} 
		if(vis[u][fr]==true)
			continue;
		vis[u][fr]=true;
		for(int i=0;i<mp[u].size();i++)
		{
			to=mp[u][i].u;
			if(fr<k)
				q.push({to,v,fr+1});
			q.push({to,v+mp[u][i].v,fr});
		}	
	}
	return 0;
}

 

 最小生成树板子

传送门P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<bits/stdc++.h>
using namespace std;
long long n,m,x,y,z,ans[5002],p,vp,ex,now,to;
bool vis[5002],fg;
struct node
{
	long long u,v;
};
vector<node>f[5002];//f[i][j].u-节点i的第j个连接点的编号  f[i][j].v-节点i的第j个连接点的权值
node temp;
bool operator<(node a,node b)
{
	return a.v>b.v;
}
priority_queue<node> q;
int main()
{
	scanf("%lld%lld",&n,&m);
	memset(vis,false,sizeof(vis));//是否在树内 
	vis[1]=true;//节点1已经加入树 
	ans[1]=1;//以1为树内第一个点 
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&x,&y,&z);
		f[x].push_back({y,z});
		f[y].push_back({x,z});
	}
	for(int i=2;i<=n;i++)
	{
		now=ans[i-1];//当前点 
		for(int j=0;j<f[now].size();j++)
			q.push({f[now][j].u,f[now][j].v});//加入与当前点相连的点 
		fg=false;//还没有找到下一个点(要求是不在树内且连入代价最低) 
		while(fg==false)	
		{
			if(q.empty()==true)//无解 
			{
				printf("orz");
				return 0;
			}
			temp=q.top();
			q.pop();
			if(vis[temp.u]==false)//与树中点相连的点不在树内,符合条件加入树 
			{
				fg=true;
				ex+=temp.v;
				ans[i]=temp.u;
				vis[temp.u]=true;
			}
		}
	}
	printf("%lld",ex);
	return 0;	
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值