UVa1660/LA3031 Cable TV Network
- 题目链接
- 题意
- 分析
- AC 代码
题目链接
本题是2004年icpc欧洲区域赛东南欧赛区的题目
题意
给定一个n(n≤50)个点的无向图,求它的点连通度,即最少删除多少个点,使得图不连通。如下图所示,a)的点连通度为3,b)的点连通度为0,c)的点连通度为2(删除1和2或者1和3)。
分析
本题可以用最小割模型建图求解,有意思的是,一开始我想出了直接解法:检测当前图是否只有一个连通分量,若不止一个连通分量则不再需要删点,否则找出要删的点并且删除之,答案计数加1。找出当前要删除的点的贪心想法:找到度最小的点后删除其邻接的度最大的那个点(度最小的点有多个时找他们邻接的度最大的那个点删)。
用最小割模型建图需要拆点,原图每个点拆成入点 i i i和出点 i + n i+n i+n并连容量为1的边 i → i + n i\rightarrow i+n i→i+n,对原图每条无向边 ( i , j ) (i,j) (i,j),连两条容量为 i n f inf inf的边 i + n → j , j + n → i i+n\rightarrow j,\;j+n\rightarrow i i+n→j,j+n→i。然后枚举所有的源点 u + n u+n u+n和汇点 v ( 0 ≤ u < v < n ) v(0≤u<v<n) v(0≤u<v<n)跑最大流,更新最小值作为答案即可。注意跑最大流前每条边的流要先归零。
AC 代码
直接解法
#include <iostream>
using namespace std;#define N 60
short u[N*N>>1], v[N*N>>1], c[N], cc[N], p[N], g[N][N], n, m;short find(short x) {return p[x]==x ? x : p[x] = find(p[x]);
}short check() {short t = 0;for (short i=0; i<n; ++i) if (c[i]>=0 && find(i)==i) ++t;return t;
}int main() {while (cin >> n >> m) {for (short i=0; i<n; ++i) cc[i] = 0, p[i] = i;for (short i=0; i<m; ++i) {short a, b; char t;cin >> t >> a >> t >> b >> t;g[a][cc[a]++] = b; g[b][cc[b]++] = a;u[i] = a; v[i] = b;p[find(a)] = find(b);}for (short i=0; i<n; ++i) c[i] = cc[i];short ans = 0;while (check() == 1) {++ ans;short cx = N, cy = -1, k;for (short i=0; i<m; ++i) if (c[u[i]]>0 && c[v[i]]>0) {short a = min(c[u[i]], c[v[i]]), b = max(c[u[i]], c[v[i]]);if (a<cx || (a==cx && b>cy)) cx = a, cy = b, k = i;}if (cy < 0) break;short y = c[u[k]] == cy ? u[k] : v[k];for (short i=0; i<cc[y]; ++i) --c[g[y][i]];c[y] = -1;for (short i=0; i<n; ++i) p[i] = i;for (short i=0; i<m; ++i) if (c[u[i]]>0 && c[v[i]]>0) p[find(u[i])] = find(v[i]);}cout << ans << endl;}
}
网络流法
#include <iostream>
#include <cstring>
using namespace std;#define N 102
struct edge {int u, v, cap, flow;} e[N*N>>1];
int g[N][N>>1], q[N], p[N], d[N], cur[N], num[N], cnt[N], c, m, n; bool vis[N];void add_edge(int u, int v, int cap) {e[c] = {u, v, cap, 0}; g[u][cnt[u]++] = c++; e[c] = {v, u, 0, 0}; g[v][cnt[v]++] = c++;
}bool bfs(int s, int t) {memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); q[0] = t; d[t] = 0; vis[t] = true;int head = 0, tail = 1;while (head < tail) {int v = q[head++];for (int i=0; i<cnt[v]; ++i) {const edge& ee = e[g[v][i]^1];if (!vis[ee.u] && ee.cap > ee.flow) vis[ee.u] = true, d[ee.u] = d[v] + 1, q[tail++] = ee.u;}}return vis[s];
}int max_flow(int s, int t) {for (int i=0; i<c; ++i) e[i].flow = 0;if (!bfs(s, t)) return 0;memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur));int u = s, flow = 0, n1 = n<<1;for (int i=0; i<n1; ++i) ++num[d[i]];while (d[s] < n1) {if (u == t) {int a = n;for (int v=t; v!=s; v = e[p[v]].u) a = min(a, e[p[v]].cap - e[p[v]].flow);for (int v=t; v!=s; v = e[p[v]].u) e[p[v]].flow += a, e[p[v]^1].flow -= a;flow += a; u = s;}bool ok = false;for (int i=cur[u]; i<cnt[u]; ++i) {const edge& ee = e[g[u][i]];if (ee.cap > ee.flow && d[u] == d[ee.v] + 1) {ok = true; p[ee.v] = g[u][i]; cur[u] = i; u = ee.v;break;}}if (!ok) {int m = n1 - 1;for (int i=0; i<cnt[u]; ++i) {const edge& ee = e[g[u][i]];if (ee.cap > ee.flow) m = min(m, d[ee.v]);}if (--num[d[u]] == 0) break;++num[d[u] = m + 1]; cur[u] = 0;if (u != s) u = e[p[u]].u;}}return flow;
}int solve() {int ans = n; char _; memset(cnt, c = 0, sizeof(cnt));for (int i=0; i<n; ++i) add_edge(i, i+n, 1);for (int i=0, u, v; i<m; ++i) cin >> _ >> u >> _ >> v >> _, add_edge(u+n, v, n), add_edge(v+n, u, n);for (int i=0; i<n; ++i) for (int j=i+1; j<n; ++j) ans = min(ans, max_flow(i+n, j));return ans;
}int main() {while (cin >> n >> m) cout << solve() << endl;return 0;
}