刘汝佳的紫书差不多就告一段落吧,我觉得可以了,怎么说呢,这书也陪着自己走了一年多了吧,也目睹了从一个啥也不会的萌新到一个稍微会一点的萌新的转变。
差不多开始下本书吧,自己也大三了,时间真的有点紧啊woctm
最喜欢的图论作为自己对这本书的谢幕,也好,也好
uva10735(欧拉回路+网络流)
题意:给一个图,图中有部分是向边,部分是无向边,要求判断是否存在欧拉回路,若存在,输出路径
解法:
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define MEM(a,x) memset(a,x,sizeof(a)) 4 #define INF 0x3f3f3f3f 5 #define MAXN 10000+10 6 using namespace std; 7 8 struct Euler 9 { 10 struct Edge { 11 int to, nxt; 12 Edge() {} 13 Edge(int x, int y) :to(x), nxt(y) {} 14 }edges[MAXN]; 15 int head[MAXN], n, E; 16 void init(const int &n) { 17 this->n = n; E = 0; 18 for (int i = 0; i <= n; i++)head[i] = -1; 19 } 20 void AddEdge(int f, int t) { 21 edges[++E] = Edge(t, head[f]); 22 head[f] = E; 23 } 24 stack<int >S; 25 bool vis[MAXN];//use when dfs 26 void dfs(int x) {//get EulerCircle 27 Edge e; 28 for (int i = head[x]; i != -1; i = e.nxt) { 29 e = edges[i]; 30 if (vis[i])continue; 31 vis[i] = 1; 32 dfs(e.to); 33 S.push(x); 34 } 35 } 36 void getEulerCircle() { 37 while (!S.empty())S.pop(); 38 memset(vis, 0, sizeof(vis)); 39 dfs(1); 40 for (; !S.empty(); S.pop()) { 41 printf("%d ", S.top()); 42 } 43 puts("1"); 44 } 45 }gg; 46 47 struct Dinic 48 { 49 struct Edge { 50 int from, to, cap, flow; 51 Edge(int u, int v, int c, int f) : 52 from(u), to(v), cap(c), flow(f) {} 53 }; 54 int n, s, t; 55 vector<Edge>edges; 56 vector< int>G[MAXN]; //gragh 57 bool vis[MAXN]; //use when bfs 58 int d[MAXN], cur[MAXN];//dist,now edge,use in dfs 59 void AddEdge(int from, int to, int cap) { 60 edges.push_back(Edge(from, to, cap, 0)); 61 edges.push_back(Edge(to, from, 0, 0)); 62 int top = edges.size(); 63 G[from].push_back(top - 2); 64 G[to].push_back(top - 1); 65 } 66 void init(int n, int s, int t) { 67 this->n = n; 68 this->s = s; 69 this->t = t; 70 edges.clear(); 71 for (int i = 0; i <= n; i++)G[i].clear(); 72 } 73 74 bool BFS() { 75 memset(vis, 0, sizeof(vis)); 76 queue<int>Q; 77 Q.push(s); d[s] = 0; vis[s] = 1; 78 while (!Q.empty()) { 79 int x = Q.front(); Q.pop(); 80 for (int i = 0; i < G[x].size(); i++) { 81 Edge &e = edges[G[x][i]]; 82 if (vis[e.to] || e.cap <= e.flow)continue; 83 vis[e.to] = 1; 84 d[e.to] = d[x] + 1; 85 Q.push(e.to); 86 } 87 } 88 return vis[t]; 89 } 90 int DFS(const int& x, int a) { 91 if (x == t || a == 0) { return a; } 92 int flow = 0, f; 93 for (int& i = cur[x]; i < G[x].size(); i++) { 94 Edge& e = edges[G[x][i]]; 95 if (d[x] + 1 != d[e.to])continue; 96 if ((f = DFS(e.to, min(a, e.cap - e.flow))) <= 0)continue; 97 e.flow += f; 98 edges[G[x][i] ^ 1].flow -= f;//ϲߍ 99 flow += f; a -= f; 100 if (!a) break; 101 } 102 return flow; 103 } 104 int MaxFlow(int s, int t) { 105 int flow = 0; 106 for (; BFS();) { 107 memset(cur, 0, sizeof(cur)); 108 flow += DFS(s, INF); 109 } 110 return flow; 111 } 112 void buildEuler(int n) { 113 for (int i = 1; i <= n; i++) { 114 for (int j = 0; j < G[i].size(); j++) { 115 Edge &e = edges[G[i][j]]; 116 if (e.to == s || e.to == t) continue; 117 if (!e.cap)continue; 118 if (e.flow == e.cap)gg.AddEdge(e.to, e.from); 119 else gg.AddEdge(e.from, e.to); 120 } 121 } 122 } 123 }g; 124 125 int d[MAXN];//degree = out - in 126 bool work(int n) { 127 int flow = 0; 128 REP(i, 1, n + 1) { 129 if (d[i] & 1)return 0; 130 if (d[i] > 0) { 131 g.AddEdge(g.s, i, d[i] >> 1); 132 flow += d[i] >> 1; 133 } 134 else if (d[i] < 0) 135 g.AddEdge(i, g.t, -(d[i] >> 1)); 136 } 137 if (flow != g.MaxFlow(0, n + 1)) return 0; 138 return 1; 139 } 140 141 int main() { 142 int T, x, y, n, m; 143 scanf("%d", &T); 144 for (char ch; T--;) { 145 scanf("%d%d", &n, &m); 146 g.init(n, 0, n + 1); 147 gg.init(n); 148 MEM(d, 0); 149 REP(i, 0, m) { 150 scanf("%d %d %c", &x, &y, &ch); 151 if (ch == 'D') gg.AddEdge(x, y); 152 else g.AddEdge(x, y, 1); 153 d[x]++; d[y]--;//Degree 154 } 155 if (!work(n))puts("No euler circuit exist"); 156 else { 157 g.buildEuler(n); 158 gg.getEulerCircle(); 159 } 160 if (T)puts(""); 161 } 162 return 0; 163 }
uva1001
题意:
给出一些球,球内的时间为零,球之间的速度为10每单位。给两个点,求最短时间。
解法:
处理出任意两点之间的距离 && 做题的时候要大胆点hhh
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define MEM(a,x) memset(a,x,sizeof(a)) 4 #define INF 0x3f3f3f3f 5 #define MAXN 100+10 6 using namespace std; 7 struct node { int x, y, z, r; }p[MAXN]; 8 double g[MAXN][MAXN]; 9 10 double dis(node a,node b) { 11 double ans = sqrt( 12 (a.x - b.x)*(a.x - b.x) 13 + (a.y - b.y)*(a.y - b.y) 14 + (a.z - b.z)*(a.z - b.z)) 15 - (a.r + b.r); 16 return ans < 0 ? 0 : ans; 17 } 18 19 int main() { 20 int n, cnt = 0; 21 while (cin >> n && n != -1) { 22 REP(i, 0, n) cin >> p[i].x >> p[i].y >> p[i].z >> p[i].r; 23 cin >> p[n].x >> p[n].y >> p[n].z; p[n].r = 0; 24 cin >> p[n + 1].x >> p[n + 1].y >> p[n + 1].z; p[n + 1].r = 0; 25 26 MEM(g, INF); 27 REP(i, 0, n + 2)g[i][i] = 0; 28 REP(i, 0, n + 2)REP(j, 0, n + 2) g[i][j] = dis(p[i], p[j]); 29 30 REP(k, 0, n + 2)REP(i, 0, n + 2)REP(j, 0, n + 2) 31 g[i][j] = min(g[i][j], g[i][k] + g[k][j]); 32 33 g[n][n + 1] *= 10; 34 printf("Cheese %d: Travel time = %.0f sec\n", ++cnt, g[n][n + 1]); 35 } 36 return 0; 37 }
uva820
题意:有一个计算机网络,输入节点数n,输入网络流源点和汇点src,des,再输入双向边数m。给出m条边的负载,求最大流。
解法:
网络流模板题
你想,流量其实就是运输物品,我们要求的就是从源点到汇点最多能运输多少单位的物品,
当物品变成水流的时候,他可以均匀的分布在所有边上,每个边上可以有的物品数就是这个边的容量,所有可行边的路径和就是我们要求的所谓“路径权值和”
这道题Dinic中的init有问题,改了一下就好了
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define MEM(a,x) memset(a,x,sizeof(a)) 4 #define INF 0x3f3f3f3f 5 #define MAXN 1000+10 6 using namespace std; 7 struct Edge { 8 int from, to, cap, flow; 9 }; 10 struct Dinic { 11 int n, m, s, t; 12 vector<Edge>edges; 13 vector<int>G[MAXN]; 14 bool vis[MAXN]; 15 int d[MAXN]; 16 int cur[MAXN]; 17 void init() { 18 edges.clear(); 19 for (int i = 0; i < MAXN; i++) G[i].clear(); 20 memset(d, 0, sizeof d); 21 } 22 void AddEdge(int from, int to, int cap) { 23 edges.push_back({ from, to, cap, 0 }); 24 edges.push_back({ to, from, 0, 0 }); 25 m = edges.size(); 26 G[from].push_back(m - 2); 27 G[to].push_back(m - 1); 28 } 29 bool BFS() { 30 int x, i; 31 memset(vis, 0, sizeof(vis)); 32 queue<int>Q; 33 Q.push(s); 34 d[s] = 0; 35 vis[s] = 1; 36 while (!Q.empty()) { 37 x = Q.front(), Q.pop(); 38 for (i = 0; i < G[x].size(); i++) { 39 Edge & e = edges[G[x][i]]; 40 if (!vis[e.to] && e.cap > e.flow) { 41 vis[e.to] = 1; 42 d[e.to] = d[x] + 1; 43 Q.push(e.to); 44 } 45 } 46 } 47 return vis[t]; 48 } 49 int DFS(int x, int a) { 50 if (x == t || a == 0) 51 return a; 52 int flow = 0, f; 53 for (int &i = cur[x]; i < G[x].size(); i++) { 54 Edge & e = edges[G[x][i]]; 55 if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) { 56 e.flow += f; 57 edges[G[x][i] ^ 1].flow -= f; 58 flow += f; 59 a -= f; 60 if (a == 0) 61 break; 62 } 63 } 64 return flow; 65 } 66 int Maxflow(int s, int t) { 67 this->s = s, this->t = t; 68 int flow = 0; 69 while (BFS()) { 70 memset(cur, 0, sizeof(cur)); 71 flow += DFS(s, INF); 72 } 73 return flow; 74 } 75 }Men; 76 int main() { 77 int n, kase = 0; 78 while (scanf("%d", &n) && n) { 79 int s, t, c, u, v, w; 80 Men.init(); 81 scanf("%d%d%d", &s, &t, &c); 82 REP(i, 0, c) { 83 scanf("%d%d%d", &u, &v, &w); 84 Men.AddEdge(u, v, w); 85 Men.AddEdge(v, u, w); 86 } 87 printf("Network %d\nThe bandwidth is %d.\n\n", ++kase, Men.Maxflow(s, t)); 88 } 89 return 0; 90 }
uva10801
题意:
一个人在大楼的0楼,大楼最多到99层。有n部电梯,每部电梯有可到达的楼层,每个电梯移动一层的时间为T[i]秒。换电梯的时间为60秒。
给你一个楼层,问你电梯到这个楼层的最短路径。
解法:
很容易看出是一个最短路问题,关键在于如何建图,
首先我们分析一下我们很容易想到把楼层当作点把时间当左边,但是有一个问题由于它涉及楼层中换电梯所以这样做的话我们就无法计算换电梯的时间,所以我的方法是把一个楼层拆成n个点(这样做的前提是在这道题中n比较小<=5)分别表示从哪一个电梯下来的。然后再把相同楼层的点之间加一条长度为60的边。
这样再通过执行n次Dijkstra算法去最小值得出答案。(或者直接Floyd)
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define MEM(a,x) memset(a,x,sizeof(a)) 4 #define INF 0x3f3f3f3f 5 #define MAXN 100+10 6 using namespace std; 7 int t[MAXN], num[MAXN]; 8 int g[MAXN][MAXN]; 9 10 void AddEdge(int i, int j, int s) { 11 g[i][j] = g[j][i] = min(min(g[i][j], g[j][i]), abs(i - j)*s); 12 return; 13 } 14 15 int main() { 16 int n, k; 17 while (scanf("%d%d", &n, &k) != EOF) { 18 MEM(g, INF); 19 REP(i, 0, n)scanf("%d", &t[i]); 20 int UP = -1; 21 REP(i, 0, n) { 22 char ch = '\0'; 23 for (int j = 0; ch != '\n'; j++) { 24 scanf("%d%c", &num[j], &ch); 25 UP = max(UP, num[j]); 26 REP(k, 0, j) AddEdge(num[j], num[k], t[i]); 27 } 28 } 29 30 UP++; 31 REP(i, 0, UP)g[i][i] = 0; 32 REP(k, 0, UP)REP(i, 0, UP)REP(j, 0, UP) 33 g[i][j] = min(g[i][j], g[i][k] + g[k][j] + 60); 34 35 if (k == 0)printf("0\n"); 36 else if (g[0][k] == INF)printf("IMPOSSIBLE\n"); 37 else printf("%d\n", g[0][k]); 38 } 39 return 0; 40 }
uva1663
题意:
给出一些01串,含星号的串表示包含两个串,星号位置分别为0和1。
每次可以消掉一个串或者两个只有一个数字不同的串,求最少几次可以消掉所有串。
解法:
读出所有串,两两判断能否一起消掉,然后其最大匹配数即可。具体细节见代码。
这道题的建图挺有意思的
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define CLR(a) memset(a,0,sizeof(a)); 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define ALL(x) begin(x),end(x) 6 #define INF 0x3f3f3f3f 7 #define MAXN 2000+10 8 #define LL long long 9 using namespace std; 10 11 int Set[MAXN], n, m; 12 char s[20]; 13 14 int g[MAXN][MAXN], match[MAXN], vis[MAXN]; 15 bool dfs(int u) { 16 for (int i = 0; i < MAXN; i++) { 17 if (g[u][i] && !vis[i]) { 18 vis[i] = 1; 19 if (match[i] == -1 || dfs(match[i])) { 20 match[i] = u; 21 return true; 22 } 23 } 24 } 25 return false; 26 } 27 28 int Hungarian(){ 29 int ans = 0; 30 memset(match, -1, sizeof(match)); 31 for (int i = 0; i < MAXN; i++){ 32 memset(vis, 0, sizeof(vis)); 33 if (dfs(i))ans++; 34 } 35 return ans; 36 } 37 38 int main() { 39 while (scanf("%d%d", &n, &m) && (n+m)) { 40 CLR(g); CLR(Set); CLR(vis); 41 REP(i, 0, m) { 42 scanf("%s", s); 43 int pos = -1, tmp = 0; 44 REP(j, 0, n) { 45 if (s[j] == '1')tmp |= 1 << j; 46 else if (s[j] == '*')pos = j; 47 } 48 Set[tmp] = 1; 49 if (pos != -1) {//将*拆分成0和1两种 50 tmp |= 1 << pos; 51 Set[tmp] = true; 52 } 53 } 54 m = 0; 55 REP(i, 0, MAXN) { 56 if (Set[i]) { 57 m++; 58 REP(j, 0, n) { 59 int tmp = i ^ (1 << j);//只有一位不同 60 if (Set[tmp])g[i][tmp] = true; 61 } 62 } 63 } 64 printf("%d\n", m - Hungarian() / 2); 65 } 66 return 0; 67 }
uva12549
题意:
在一个Y行X列的网格里有空地(.),重要位置(*)和障碍物(#),用最少的机器人看守所有重要位置,每个机器人要放在一个格子里,面朝上下左右4个方向之一。
机器人会发出激光,一直射到障碍物为止,沿途都是看守范围。
解法:
首先肯定是二分图最大匹配;其次如果没有障碍物的话,直接每个目标的x和y建表跑匈牙利即可;
但这个题目中他有障碍物,我们就横纵坐标分块即可(相当于离散化),之后匈牙利
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define CLR(a) memset(a,0,sizeof(a)); 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define ALL(x) begin(x),end(x) 6 #define INF 0x3f3f3f3f 7 #define MAXN 100+10 8 #define MAXM 2005 9 #define LL long long 10 using namespace std; 11 12 int g[MAXM][MAXM], match[MAXM], vis[MAXM]; 13 int mp[MAXN][MAXN], X[MAXN][MAXN], Y[MAXN][MAXN]; 14 int xcnt, ycnt; 15 bool dfs(int u) { 16 for (int i = 0; i < ycnt; i++) { 17 if (g[u][i] && !vis[i]) { 18 vis[i] = 1; 19 if (match[i] == -1 || dfs(match[i])) { 20 match[i] = u; 21 return true; 22 } 23 } 24 } 25 return false; 26 } 27 28 int Hungarian() { 29 int ans = 0; 30 memset(match, -1, sizeof(match)); 31 for (int i = 0; i < xcnt; i++) { 32 memset(vis, 0, sizeof(vis)); 33 if (dfs(i))ans++; 34 } 35 return ans; 36 } 37 38 int main() { 39 int T; scanf("%d", &T); 40 while (T--) { 41 CLR(g); CLR(mp); CLR(X); CLR(Y); 42 getchar(); 43 int n, m; scanf("%d%d", &n, &m); 44 45 int p, x, y; 46 scanf("%d", &p); 47 REP(i, 0, p) { scanf("%d %d", &x, &y); mp[x-1][y-1] = 1; } 48 scanf("%d", &p); 49 REP(i, 0, p) { scanf("%d %d", &x, &y); mp[x-1][y-1] = 2; } 50 51 xcnt = -1; 52 REP(i, 0, n) { 53 bool flag = true; 54 REP(j, 0, m) { 55 if (mp[i][j] == 1) { 56 if (flag)xcnt++; 57 X[i][j] = xcnt; 58 flag = false; 59 } 60 else if(mp[i][j]==2) 61 flag = true; 62 } 63 } 64 ycnt = -1; 65 REP(i, 0, n) { 66 bool flag = true; 67 REP(j, 0, m) { 68 if (mp[j][i] == 1) { 69 if (flag)ycnt++; 70 Y[j][i] = ycnt; 71 flag = false; 72 } 73 else if (mp[j][i] == 2) 74 flag = true; 75 } 76 } 77 xcnt++, ycnt++; 78 79 REP(i, 0, n)REP(j, 0, m)if(mp[i][j]==1) 80 g[X[i][j]][Y[i][j]] = 1; 81 printf("%d\n", Hungarian()); 82 } 83 return 0; 84 }
uva1664
题意:
给你n个点,接着给你n-1条边形成一颗生成树,每条边都有一个权值。
求的是以一个点作为特殊点,并求出从此点出发到其他每个点的条件边权的总和最大,条件边权就是:起点到终点经过的权值的最小值。
解法:
非常巧妙的并查集;真的没有想到;
首先我们把要选的那个结点看作是我们的根节点,用Kruscal的思想,首先给所有边按照从大到小的顺序排序,
之后我们遍历每条边,该边的两个顶点所在的集合设为A、B,集合A、B的顶点a、b,要让谁当中心点呢?
易知:无论谁当中心点,它与另一个集合中任一点的容量都为该边长度(因为是从大到小枚举的)。
那么为了求出总容量,我们要维护一些值,用空间换时间 。
维护每个顶点的总容量sum[i],维护每个顶点与之相连的顶点数量,cnt[i],当前答案ans 。
那么对于a、b点,如果以a为中心,总容量为sum[a] + cnt[b] * e[i].c 。
反之亦然,哪个量大,则以哪个点为城市中心,也就是并查集的根结点 。
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define CLR(a) memset(a,0,sizeof(a)); 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define ALL(x) begin(x),end(x) 6 #define LL long long 7 const int INF = 0x3f3f3f3f; 8 const int MAXN = 200000 + 10; 9 using namespace std; 10 11 struct edge { 12 int u, v, w; 13 bool operator < (const edge& rhs) const { 14 return w > rhs.w; 15 } 16 }e[MAXN]; 17 LL p[MAXN], sum[MAXN], cnt[MAXN]; 18 int findSet(int x) { return p[x] == x ? x : p[x] = findSet(p[x]); } 19 int main() { 20 int n; 21 while (scanf("%d", &n) != EOF) { 22 REP(i, 0, n - 1) {scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);} 23 REP(i, 1, n+1) {p[i] = i; sum[i] = 0; cnt[i] = 1;} 24 sort(e, e + n - 1); 25 LL ans = 0; 26 REP(i, 0, n - 1) { 27 int x = findSet(e[i].u), y = findSet(e[i].v); 28 LL sumu = sum[x] + cnt[y] * e[i].w; 29 LL sumv = sum[y] + cnt[x] * e[i].w; 30 if (sumu < sumv) { 31 p[x] = y; cnt[y] += cnt[x]; 32 ans = sum[y] = sumv; 33 } 34 else { 35 p[y] = x; cnt[x] += cnt[y]; sum[x] = sumu; 36 ans = sum[x] = sumu; 37 } 38 } 39 printf("%lld\n", ans); 40 } 41 return 0; 42 }
uva1668(建图)
题意:
给定一棵有 nn 个节点的树,每条边有边权 w(u,v)w(u,v),用最小的路径覆盖所有的边,使得每条边被覆盖的次数等于其边权。
解法:
这道题考的是思维。
首先我们求出所有点的权值之和,就是我们要求的答案的最大值;再求出每个点的最大权值,依次看其是否超过总权值的一半;
如果超过的话,我们就直接减去最大权值,代表不能全部合并; 否则,我们就减去总权值的一半,代表能全部合并;
1 #include<bits/stdc++.h> 2 #define REP(i, a, b) for(int i = (a); i < (b); i++) 3 #define CLR(a) memset(a,0,sizeof(a)); 4 #define MEM(a,x) memset(a,x,sizeof(a)) 5 #define ALL(x) begin(x),end(x) 6 #define LL long long 7 #define INF 0x3f3f3f3f 8 #define MAXN 100000 + 10 9 using namespace std; 10 11 int deg[MAXN], maxd[MAXN]; 12 int main() { 13 int T; scanf("%d", &T); 14 int kase = 0; 15 while (T--) { 16 CLR(deg); CLR(maxd); 17 int n; scanf("%d", &n); 18 int u, v, w; 19 LL ans = 0; 20 REP(i, 1, n) { 21 scanf("%d%d%d", &u, &v, &w); 22 deg[u] += w, deg[v] += w; 23 maxd[u] = max(maxd[u], w); 24 maxd[v] = max(maxd[v], w); 25 ans += w; 26 } 27 REP(i, 1, n+1) { 28 if ((maxd[i] << 1) <= deg[i]) 29 ans -= deg[i] >> 1; 30 else 31 ans -= deg[i] - maxd[i]; 32 } 33 printf("Case #%d: %lld\n", ++kase, ans); 34 } 35 return 0; 36 }