图论总结
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条无向边&#