Description
Input
Output
题目大意:有n(n≤50)个点,起点1到终点n,有m条有向边(m≤4000)。现破坏掉若干起点和终点以外的点,使得从起点到终点经过的边数必须大于k条。问最少要破坏多少个点,保证从起点到终点没有边。
我们先来看一个可以AC但实际上错误的思路o(╯□╰)o(为什么错误还能AC啊?数据弱呗……)
思路:先求每个点到起点和终点的最短路径,然后每个点拆成两个点x、x',如果dis(s,x) + dis(x,t) ≤ k,那么建一条边x→x',容量为1(源点和汇点容量为无穷大)。对每条边(i, j),连一条边i'→j,容量为无穷大。求最小割。根据最大流最小割定理,最大流为答案。因为对于一点x,如果dis(s,x) + dis(x,t) > k,那么没必要破坏点x。那么问题就变成了最少破坏多少个点,使得从1到n必须要经过一个点,经过那个点的话从1到n必然会大于k。
先上代码(15MS):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 7 const int MAXN = 210; 8 const int MAXE = 20010; 9 const int INF = 0x3fff3fff; 10 11 struct SAP { 12 int head[MAXN], cur[MAXN], pre[MAXN], gap[MAXN], dis[MAXN]; 13 int to[MAXE], cap[MAXE], flow[MAXE], next[MAXE]; 14 int ecnt, n, st, ed; 15 16 void init() { 17 memset(head, 0, sizeof(head)); 18 ecnt = 2; 19 } 20 21 void add_edge(int u, int v, int c) { 22 to[ecnt] = v; cap[ecnt] = c; flow[ecnt] = 0; next[ecnt] = head[u]; head[u] = ecnt++; 23 to[ecnt] = u; cap[ecnt] = 0; flow[ecnt] = 0; next[ecnt] = head[v]; head[v] = ecnt++; 24 //printf("%d->%d %d\n", u, v, c); 25 } 26 27 void bfs() { 28 memset(dis, 0x3f, sizeof(dis)); 29 queue<int> que; que.push(ed); 30 dis[ed] = 0; 31 while(!que.empty()) { 32 int u = que.front(); que.pop(); 33 ++gap[dis[u]]; 34 for(int p = head[u]; p; p = next[p]) { 35 int v = to[p]; 36 if(dis[v] > n && cap[p ^ 1]) { 37 dis[v] = dis[u] + 1; 38 que.push(v); 39 } 40 } 41 } 42 } 43 44 int Max_flow(int ss, int tt, int nn) { 45 st = ss, ed = tt, n = nn; 46 int ans = 0, minFlow = INF, u; 47 for(int i = 0; i <= n; ++i) { 48 cur[i] = head[i]; 49 gap[i] = 0; 50 } 51 u = pre[st] = st; 52 bfs(); 53 while(dis[st] < n) { 54 bool flag = false; 55 for(int &p = cur[u]; p; p = next[p]) { 56 int v = to[p]; 57 if(cap[p] > flow[p] && dis[v] + 1 == dis[u]) { 58 flag = true; 59 minFlow = min(minFlow, cap[p] - flow[p]); 60 pre[v] = u; 61 u = v; 62 if(u == ed) { 63 ans += minFlow; 64 while(u != st) { 65 u = pre[u]; 66 flow[cur[u]] += minFlow; 67 flow[cur[u] ^ 1] -= minFlow; 68 } 69 minFlow = INF; 70 } 71 break; 72 } 73 } 74 if(flag) continue; 75 int minDis = n - 1; 76 for(int p = head[u]; p; p = next[p]) { 77 int v = to[p]; 78 if(cap[p] > flow[p] && dis[v] < minDis) { 79 minDis = dis[v]; 80 cur[u] = p; 81 } 82 } 83 if(--gap[dis[u]] == 0) break; 84 gap[dis[u] = minDis + 1]++; 85 u = pre[u]; 86 } 87 return ans; 88 } 89 } G; 90 91 struct SP { 92 int head[MAXN], head2[MAXN], dis_st[MAXN], dis_ed[MAXN]; 93 int to[MAXE], next[MAXE], to2[MAXE], next2[MAXE]; 94 int ecnt, n, st, ed; 95 96 void init(int ss, int tt, int nn) { 97 memset(head, 0, sizeof(head)); 98 memset(head2, 0, sizeof(head2)); 99 ecnt = 2; 100 st = ss; ed = tt; n = nn; 101 } 102 103 void add_edge(int u, int v) { 104 to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt; 105 to2[ecnt] = u; next2[ecnt] = head2[v]; head2[v] = ecnt++; 106 } 107 108 void make_dis_st() { 109 memset(dis_st, 0x3f, sizeof(dis_st)); 110 queue<int> que; que.push(st); 111 dis_st[st] = 0; 112 while(!que.empty()) { 113 int u = que.front(); que.pop(); 114 for(int p = head[u]; p; p = next[p]) { 115 int v = to[p]; 116 if(dis_st[v] > n) { 117 dis_st[v] = dis_st[u] + 1; 118 que.push(v); 119 } 120 } 121 } 122 } 123 124 void make_dis_ed() { 125 memset(dis_ed, 0x3f, sizeof(dis_ed)); 126 queue<int> que; que.push(ed); 127 dis_ed[ed] = 0; 128 while(!que.empty()) { 129 int u = que.front(); que.pop(); 130 for(int p = head2[u]; p; p = next2[p]) { 131 int v = to2[p]; 132 if(dis_ed[v] > n) { 133 dis_ed[v] = dis_ed[u] + 1; 134 que.push(v); 135 } 136 } 137 } 138 } 139 140 void make_G(int k) { 141 make_dis_st(); 142 //for(int i = 1; i <= n; ++i) printf("%d ", dis_st[i]); 143 make_dis_ed(); 144 //for(int i = 1; i <= n; ++i) printf("%d ", dis_ed[i]); 145 G.init(); 146 G.add_edge(1, 1 + n, INF); 147 G.add_edge(n, n + n, INF); 148 for(int i = 2; i < n; ++i) 149 if(dis_st[i] + dis_ed[i] <= k) G.add_edge(i, i + n, 1); 150 for(int u = 1; u <= n; ++u) { 151 for(int p = head[u]; p; p = next[p]) { 152 int v = to[p]; 153 G.add_edge(u + n, v, INF); 154 } 155 } 156 } 157 } T; 158 159 int n, m, k, a, b; 160 161 int main() { 162 while(scanf("%d%d%d", &n, &m, &k) != EOF) { 163 if(n == 0 && m == 0 && k == 0) break; 164 T.init(1, n, n); 165 while(m--) { 166 scanf("%d%d", &a, &b); 167 T.add_edge(a, b); 168 } 169 T.make_G(k); 170 printf("%d\n", G.Max_flow(1, n + n, n + n)); 171 } 172 }
但是这样做是错的,为什么呢?我们来看一个Discuss里的数据:
8 10 5
1 2
2 3
3 4
4 5
5 6
6 8
1 7
7 8
4 7
7 4
这个数据输出应该是1(破坏点7),但是上面的代码会输出2。因为上面的思路忽略了一点:当我们破坏掉某个点的时候,经过另一些从起点到终点的距离可能会变化以至于大于k。
那么怎么办呢?我们只能退而求其次o(╯□╰)o,虽然这也是一个能AC但是错的思路
思路:每个点拆成两个点x、x'(还是拆点╮(╯▽╰)╭),然后建一条边x→x',容量为1(源点和汇点为无穷大),费用为0。然后对每条边(i, j)建一条边,容量为无穷大,费用为1。那么不断增广直到费用大于k时停止增广,这是流量就是答案(还是求流╮(╯▽╰)╭)。
上代码(46MS):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 7 const int MAXN = 222; 8 const int MAXE = 10010; 9 const int INF = 0x3f3f3f3f;//can't modify 10 11 int n, m, k, a, b; 12 13 struct MinCostFlow { 14 bool vis[MAXN]; 15 int head[MAXN], dis[MAXN], pre[MAXN]; 16 int to[MAXE], next[MAXE], cost[MAXE], flow[MAXE]; 17 int n, st, ed, ecnt; 18 19 void init(int ss, int tt, int nn) { 20 memset(head, 0, sizeof(head)); 21 ecnt = 2; 22 st = ss, ed = tt, n = nn; 23 } 24 25 void add_edge(int u, int v, int c, int f) { 26 to[ecnt] = v; cost[ecnt] = f; flow[ecnt] = c; next[ecnt] = head[u]; head[u] = ecnt++; 27 to[ecnt] = u; cost[ecnt] = -f; flow[ecnt] = 0; next[ecnt] = head[v]; head[v] = ecnt++; 28 } 29 30 bool spfa() { 31 memset(vis, 0, sizeof(vis)); 32 memset(dis, 0x3f, sizeof(dis)); 33 queue<int> que; que.push(st); 34 vis[st] = true; dis[st] = 0; 35 while(!que.empty()) { 36 int u = que.front(); que.pop(); 37 vis[u] = false; 38 for(int p = head[u]; p; p = next[p]) { 39 int v = to[p]; 40 if(flow[p] && dis[v] > dis[u] + cost[p]) { 41 dis[v] = dis[u] + cost[p]; 42 pre[v] = p; 43 if(!vis[v]) { 44 vis[v] = true; 45 que.push(v); 46 } 47 } 48 } 49 } 50 return dis[ed] <= k; 51 } 52 53 void min_cost_flow(int &minFlow, int &fee) { 54 minFlow = fee = 0; 55 while(spfa()) { 56 fee += dis[ed]; 57 int u = ed, tmp = INF; 58 while(u != st) { 59 tmp = min(tmp, flow[pre[u]]); 60 u = to[pre[u] ^ 1]; 61 } 62 u = ed; 63 while(u != st) { 64 flow[pre[u]] -= tmp; 65 flow[pre[u] ^ 1] += tmp; 66 u = to[pre[u] ^ 1]; 67 } 68 minFlow += tmp; 69 } 70 } 71 72 int mincost() { 73 int ret, tmp; 74 min_cost_flow(tmp, ret); 75 return ret; 76 } 77 78 int maxflow() { 79 int ret, tmp; 80 min_cost_flow(ret, tmp); 81 return ret; 82 } 83 } G; 84 85 86 int main() { 87 while(scanf("%d%d%d", &n, &m, &k) != EOF) { 88 if(n == 0 && m == 0 && k == 0) break; 89 G.init(1, n * 2, n * 2); 90 G.add_edge(1, 1 + n, INF, 0); 91 G.add_edge(n, n + n, INF, 0); 92 for(int i = 2; i < n; ++i) G.add_edge(i, i + n, 1, 0); 93 while(m--) { 94 scanf("%d%d", &a, &b); 95 G.add_edge(a + n, b, INF, 1); 96 } 97 printf("%d\n", G.maxflow()); 98 } 99 }
10 11 5
1 2
2 3
3 4
4 5
5 10
2 9
1 6
6 7
7 8
8 9
9 10
然后上面的代码就过不了这组数据o(╯□╰)o,代码输出1,正确输出为2,怪不得我想不明白为什么是对的o(╯□╰)o(这数据敢不敢再弱一点……)
最后我们只能再退而求其次了o(╯□╰)o,暴力枚举答案,然后枚举最短路径上的点,深搜,再枚举删点后最短路径上的点,再深搜……
搜索(484MS,慢了点但起码是对了╮(╯▽╰)╭):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 7 const int MAXN = 110; 8 const int MAXE = 4010; 9 const int INF = 0x3fff3fff; 10 11 int n, m, k, a, b, ans, ecnt; 12 int SP[MAXN][MAXN]; 13 int head[MAXN], dis[MAXN], pre[MAXN]; 14 int to[MAXE], next[MAXE]; 15 bool del[MAXN]; 16 17 void init() { 18 memset(head, 0, sizeof(head)); 19 memset(del, 0, sizeof(del)); 20 ecnt = 2; 21 } 22 23 void add_edge(int u, int v) { 24 to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++; 25 } 26 27 bool bfs() { 28 memset(dis, 0x3f, sizeof(head)); 29 queue<int> que; que.push(1); 30 dis[1] = 0; 31 while(!que.empty()) { 32 int u = que.front(); que.pop(); 33 for(int p = head[u]; p; p = next[p]) { 34 int v = to[p]; 35 if(!del[v] && dis[v] > n) { 36 dis[v] = dis[u] + 1; 37 pre[v] = u; 38 if(v == n) return dis[n] <= k; 39 que.push(v); 40 } 41 } 42 } 43 return false; 44 } 45 46 bool flag; 47 48 void dfs(int dep) { 49 if(!bfs()) { 50 flag = true; 51 return ; 52 } 53 if(dep > ans) return ; 54 int u = pre[n], cnt = 0; 55 while(u != 1) { 56 SP[dep][cnt++] = u; 57 u = pre[u]; 58 } 59 for(int i = cnt - 1; i >= 0; --i) { 60 del[SP[dep][i]] = true; 61 dfs(dep + 1); 62 del[SP[dep][i]] = false; 63 } 64 } 65 66 int main() { 67 while(scanf("%d%d%d", &n, &m, &k) != EOF) { 68 if(n == 0 && m == 0 && k == 0) break; 69 init(); 70 while(m--) { 71 scanf("%d%d", &a, &b); 72 add_edge(a, b); 73 } 74 flag = false; 75 for(ans = 0; ans < n; ++ans) { 76 dfs(1); 77 if(flag) break; 78 } 79 printf("%d\n", ans); 80 } 81 }