图论总结

图论总结


1.最小生成树:在稀疏图中用kruskal,在稠密图中用prim
2.涉及排名先后时,可考虑topsort
3.单源最短路问题数据范围一般为1e5,如果N是1000,甚至更小,可能就要枚举某些边了。

最短路

1.双关键字最短路。

POJ 1724 ROADS
题意:
N个城市,编号1到N。城市间有R条单向道路。
每条道路连接两个城市,有长度和过路费两个属性。
Bob只有K块钱,他想从城市1走到城市N。问最短共需要走多长的路。如果到不了N,输出-1
2<=N<=100
0<=K<=10000
1<=R<=10000
每条路的长度 L, 1 <= L <= 100
每条路的过路费T , 0 <= T <= 100

以距离为第一关键字,花费为第二关键字跑dijkstra,只要钱数允许就进行转移。

struct node{
	int now,dis,co;
	bool operator < (const node &a) const	{
		if(dis != a.dis) return dis > a.dis;
		return co > a.co;
	}
};
priority_queue <node> q;//以距离为第一关键字,花费为第二关键字排序。
void dijkstra(){
	node st;st.dis = 0;st.co = 0;st.now =  1;q.push(st);
	while(!q.empty())	{
		node no = q.top();int u = no.now;q.pop();
		if(u == n)	{printf("%d",no.dis);return;}
		for(int i = head[u]; i != -1; i = e[i].next){
			int v = e[i].v;
			//钱数允许就更新。有很多状态,所以此题数据范围小。
			if(no.co + e[i].w <= cost)	{
				node cs;
				cs.now = v;
				cs.dis = no.dis + e[i].len;
				cs.co = no.co + e[i].w;
				q.push(cs);
			}
		}
	}
	printf("-1");
}

2.同余最短路

HDU - 6071 Lazy Running
【题目大意】给出四个点1,2,3,4,1和2,2和3,3和4,4和1 之间有路相连,现在从2点出发,最后回到2点,要求路径大于等于K,问路径长度最短是多少

同余最短路:又是一个可能出现无限向下走的题目。面对这种问题有一个比较常见的处理方法就是利用同余。

题目要求的是回路,回路有这样一个性质,任意两个回路可以连接构成一个新的回路。于是任意一个回路就可以表示成x+n*y的形式,其中x和y是两个回路。现在再回到利用同余防止循环,如果我们固定y(代码中用的变量名为m),那么对于任意两个模y同余的x的效果是相同的,我们只需要保留最小的那个即可。

显然时间复杂度和y正相关,那么我们就取满足题意的最小回路作为y,然后利用最短路算法找到所有起点为1的相互关于y不同余的环,更新答案即可。在最短路的过程中,对于每一个点也只需要保留关于y不同余的路径即可。

具体做法:取与2相连的权值最小的边w。

若存在一条从起点到终点的长度为k的路径,那么必然存在一条长度为k+2w的路径,只要一开始在那条边上往返走就好了。

设dij表示从起点到i,路径长度模2w为j时,路径长度的最小值。

用最短路算法求出所有dij,然后检查d[n][k%2w]是否不超过k即可。

对于求大于等于k的最小解,只要枚举W解不等式即可。

3.枚举部分边

HDU - 3499 Flight
题意:ShuaShua要从一个城市到另一个城市,给出每两城市之间的花费(有向),ShuaShua可以有一次半价的机会,求最小花费。N<=100000

最短路径问题。

开始想到求出最短路后选择其中最大的边/2,但其实是错误的。反例:路径1:1 1 100 路径2:30 30 30

因此我们可以换一种思路,以起点和终点为单源分别求出到各点的最短路,然后枚举每一条边作为中间边,dis[u]+w(u,v)/2+diss[v]的最小值为解。

破坏道路 51Nod - 1444
在某一个国家,那儿有n个城市,他们通过m条双向道路相连。城市从1到n编号。如果城市a和b通过一条道路直接相连,那么他们之间的距离就是一个小时。这个国家的道路网络可以允许你从任意一个城市到达另外的城市。
现在你要破坏尽可能多的道路,但是要保证从城市s1到t1不超过l1小时,并且从城市s2到t2不超过l2小时。
输出最多可以破坏的道路数目,如果没有解,请输出-1。(1 ≤ n ≤ 3000, n-1 ≤ m ≤ min(3000,n*(n-1)/2) )

n的范围太小,就想到了O(n^2)的枚举。枚举两条最短路重叠的部分(路径)。又因为一条道路的距离是一小时,所以直接bfs就可以求最短路了。

queue<int> q;
void bfs(int s){
	memset(vis,0,sizeof(vis));
	q.push(s);
	dis[s][s] = 0;vis[s] = 1;
	while(!q.empty()){
		int u = q.front();q.pop();
		for(int i = head[u]; i != -1; i = e[i].nxt){
			int v = e[i].v;
			if(!vis[v] && dis[s][v] > dis[s][u] + 1){
				dis[s][v] = dis[s][u] + 1;
				q.push(v);	vis[v] = 1;
			}
		}
	}
}
int main(){
	memset(head,-1,sizeof(head));
	memset(dis,inf,sizeof(dis));
	n = read();m = read();
	for(int i = 1; i <= m; ++i){
		a = read();b = read();
		Adde(a,b);Adde(b,a);
	}
	s1 = read(); t1 = read(); l1 = read();
	s2 = read(); t2 = read(); l2 = read();
	for(register int i = 1; i <= n; ++i)	bfs(i);
	if(dis[s1][t1] > l1 || dis[s2][t2] > l2) {
		printf("-1\n");
		return 0;
	}
	ans = dis[s1][t1] + dis[s2][t2];
	
	for(register int i = 1; i <= n; ++i){
		for(register int j = 1; j <= n; ++j){
			int tmp1 = dis[s1][i] + dis[i][j] + dis[j][t1];
			int tmp2 = dis[s2][i] + dis[i][j] + dis[j][t2];
			if(tmp1 <= l1 && tmp2 <= l2)
				ans = min(ans,tmp1 + tmp2 - dis[i][j]);

			tmp2 = dis[s2][j] + dis[j][i] + dis[i][t2];
			if(tmp1 <= l1 && tmp2 <= l2)
				ans = min(ans,tmp1 + tmp2 - dis[i][j]);
		}
	}
	printf("%d\n",m - ans);
	return 0;
}

4.反向考虑贡献

POJ 3013 Big Christmas Tree
题目大意:有一颗N个点m条边的图。图中的每个节点都有它的重量,每条边的单价不同。一条边的价格是(该边连接的所有子节点的权重的总和)×(边的价格)。
求使用所有节点形成一棵以1为根树的最低成本。如果无法形成树则输出“wrong answer”

考虑计算每条边被累加了多少次,不好计算。于是反过来计算每个点的点权累加了多少次:从1到该点的最短路的条数次。每次就是点权乘上一条边的边权。就是最短路模板了。

5.建立虚点

存在多个点的意义相同,且对他们的处理方式类似时,可以采用建立虚点的方式,优化时间复杂度。

The Shortest Path in Nya Graph HDU - 4725
题意:n个点,m条无向边&#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值