图论(最短路/最小生成树/拓扑排序)

(网课题单)

A CF601A The Two Routes

一定有一种交通工具存在一条路径连接 1,n,于是对另一种交通工具跑最短路即可。

点击查看代码
#include <bits/stdc++.h>
//#define int long long
using namespace std;
int n,m,x,y,i,j,k,dis[405][405],sum;
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	memset(dis,0x3f,sizeof(dis));
	for (i=1;i<=m;i++){
		cin>>x>>y,dis[x][y]=1,dis[y][x]=1;
		sum+=((x==1 && y==n) || (x==n && y==1));
	}
	if (sum!=0)
		for (i=1;i<=n;i++)
			for (j=1;j<=n;j++)
				if (dis[i][j]==1) dis[i][j]=dis[0][0];
				else dis[i][j]=1;
	for (k=1;k<=n;k++)
		for (i=1;i<=n;i++)
			for (j=1;j<=n;j++)
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	return cout<<(dis[1][n]==dis[0][0]?-1:dis[1][n]),0;
}

B CF59E Shortest Path

BFS 的同时记录上一个经过的点即可。记录前驱的时候记录边,不是点。

启示:BFS 等算法同样是解决最短路问题的有效手段。

点击查看代码
#include <bits/stdc++.h>
//#define int long long
using namespace std;
int n,m,T,i,x,y,z;
struct Edge{
	int to,nex,dis,pre,id;
}e[40005];
int h[3005],cnt;
void add_edge(int x,int y){
	e[++cnt].to=y,e[cnt].nex=h[x],e[cnt].id=cnt,h[x]=cnt;
}
struct node{
	int pre,now,step,id;
} ;
queue <node> Q;
map <pair<pair<int,int> ,int> ,bool> c;
void go(int x){
	if (e[x].pre) go(e[x].pre);
	cout<<' '<<e[x].to;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m>>T;
	for (i=1;i<=m;i++)
		cin>>x>>y,add_edge(x,y),add_edge(y,x);
	for (i=1;i<=T;i++)
		cin>>x>>y>>z,c[make_pair(make_pair(x,y),z)]=true;
	Q.push(node{0,1,0,0});//pre now step id
	while (!Q.empty()){
		node x=Q.front();
		Q.pop();
		if (x.now==n){
			cout<<x.step<<'\n'<<1;
			return go(x.id),0;
		}
		for (i=h[x.now]; i; i=e[i].nex){
			if (c[make_pair(make_pair(x.pre,x.now),e[i].to)]) continue;
			if (e[i].dis) continue;
			e[i].dis=x.step+1,e[i].pre=x.id;
			Q.push((node){x.now,e[i].to,x.step+1,i});
		}
	}
	return cout<<-1,0;
}

C CF1196F K-th Path

k400,从这方面入手,取最短的 k 条边,至多 2k 个点做最短路,可以证明答案一定在其中。

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct Edge{int x,y,z;} e[200005];
bool cmp(Edge x,Edge y){return x.z<y.z;}
priority_queue <int,vector<int>,greater<int> > Q;
int n,m,k,i,j,tot,f[805][805],rnk[200005],vis[200005];
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m>>k;
	for (i=1;i<=m;i++) cin>>e[i].x>>e[i].y>>e[i].z;
	if (m>k) sort (e+1,e+1+m,cmp);
	for (i=1;i<=min(m,k);i++) vis[e[i].x]=1,vis[e[i].y]=1;
	for (i=1;i<=n;i++)
		if (vis[i]) rnk[i]=(++tot);
	memset(f,0x3f,sizeof(f));
	for (i=1;i<=min(m,k);i++)
		f[rnk[e[i].x]][rnk[e[i].y]]=f[rnk[e[i].y]][rnk[e[i].x]]=e[i].z;
	for (int _=1;_<=tot;_++)
		for (i=1;i<=tot;i++)
			for (j=1;j<=tot;j++)
				f[i][j]=min(f[i][j],f[i][_]+f[_][j]);
	for (i=1;i<=tot;i++)
		for (j=i+1;j<=tot;j++)
			Q.push(f[i][j]);
	for (i=1;i<k;i++) Q.pop();
	return cout<<Q.top(),0;
}

D CF1307D Cow and Fields

从起点(结果记为 dis0,i)、终点(结果记为 dis1,i)做两遍最短路,按 dis0,i 给点集排序,贪心做一下。

Record.

E CF1242B 0-1 MST

答案为用 0 链接的联通块数量减一。用并查集维护(包括包含节点个数),枚举 i(1n),根据每个联通块的大小和其与 i 相连的边为 1 的节点数即可判断该连痛块能否与 i 合并。

并查集一定要路径压缩 + 按秩合并。

可以证明(本蒟蒻不太会)时间复杂度为 O(nlogn+m)

Record.

F CF160D Edges in MST

不会思路 + 不会实现。

G CF1468J Road Reform & H [HAOI2006]聪明的猴子 & I [SCOI2005]繁忙的都市

最小生成树板子随便改一下。简单,懒得贴代码。

J 最短路(未知来源)

N 个点,M 条边的有向图,求点 1 到点 N 的最短路(保证存在)。

1N10000001M10000000

正解:Dij + 配对堆。

普通堆优化:题目中为了防止读入时超时有随机边,注释即可。

K [HAOI2016]食物链

简单的 topo 加一点点数学,坑点在于:注意单独的一种孤立生物不算一条食物链

Record.

L[SDOI2010]大陆争霸

LYH 吊打了。

最短路 + topo(在做最短路的同时做一下 topo),开三个数组分别表示最短路 distopo 的结果 f,答案 ansdisfmax)。

Record.

O ther:[JLOI2011] 飞行路线

经典分层图。gugu。。