Risk uva 12264(最大流,二分法,拆点法)
2017-04-06 00:38
399 查看
给n个点的无权无向图(n<=100),每个点有一个非负数ai。若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵。保证至少有一个属于你的点与敌方的点相邻。你可以让你的每个士兵最多移动一次,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵,使得最薄弱的关口尽量坚固。关口是指与敌方点相邻的点,薄弱与坚固分别指兵少与兵多。
我参考了这篇博客上的一些讲解。http://www.voidcn.com/blog/a197p/article/p-4252874.html
思路:把每个点拆成两个点,一个入度,一个出度,入度向自己的和每个相邻的点的出度连一条边,容量是ai,每个点出度连一条边到汇点,容量为1,那些与敌人相邻的点再多连一条边到汇点,容量是二分的值,我们只需要二分这个值,跑一下网络流,如果满流,表示可以,否则不行。
本篇通过第二个样例讲解思路,下图是第二个样例的建图结果。
敌方的点不需要参与建图,因此图中没有6,7号点。
INF指无穷,mid指二分的中值。
Q:为何需要拆点?
A:我们希望通过拆点来实现每个士兵最多移动一次。下图中每个INF的边都是士兵移动的边,观察后可以发现如此建图士兵只能从入点移动到另一个点的出点,因此最多只能移动一次。(根据题意每次最远移动到相邻的点)
Q:为何出点需要连一条容量为1的边到汇点?
A:为了保证己方的点至少有1个士兵,观察下图 1出 与 2出 满流时说明1和2点至少1个人。(此题中建立网络流模型,并且以满流作为目标,调整模型(容量),由此使满流模型满足条件,容量即是解)
Q:为何要二分?
A:最小中最大值;结果的可能取值是一个连续范围
我们要让最薄弱的关口尽量坚固,就得使关口的士兵分配得尽量均匀,然而最大流的算法不能保证分配均匀,我们只好一个个的试,满载就放宽条件,不满载就严加条件,直到试出可行的最大解。(考虑一下为什么两条边都设为mid)
至少一个士兵的处理,可以直接对于各个点需要几个士兵向汇点连边,即边界点需要二分数值的士兵,而其他需要1个士兵;也可以一开始就把各点的士兵数都-1.
我参考了这篇博客上的一些讲解。http://www.voidcn.com/blog/a197p/article/p-4252874.html
思路:把每个点拆成两个点,一个入度,一个出度,入度向自己的和每个相邻的点的出度连一条边,容量是ai,每个点出度连一条边到汇点,容量为1,那些与敌人相邻的点再多连一条边到汇点,容量是二分的值,我们只需要二分这个值,跑一下网络流,如果满流,表示可以,否则不行。
本篇通过第二个样例讲解思路,下图是第二个样例的建图结果。
敌方的点不需要参与建图,因此图中没有6,7号点。
INF指无穷,mid指二分的中值。
Q:为何需要拆点?
A:我们希望通过拆点来实现每个士兵最多移动一次。下图中每个INF的边都是士兵移动的边,观察后可以发现如此建图士兵只能从入点移动到另一个点的出点,因此最多只能移动一次。(根据题意每次最远移动到相邻的点)
Q:为何出点需要连一条容量为1的边到汇点?
A:为了保证己方的点至少有1个士兵,观察下图 1出 与 2出 满流时说明1和2点至少1个人。(此题中建立网络流模型,并且以满流作为目标,调整模型(容量),由此使满流模型满足条件,容量即是解)
Q:为何要二分?
A:最小中最大值;结果的可能取值是一个连续范围
我们要让最薄弱的关口尽量坚固,就得使关口的士兵分配得尽量均匀,然而最大流的算法不能保证分配均匀,我们只好一个个的试,满载就放宽条件,不满载就严加条件,直到试出可行的最大解。(考虑一下为什么两条边都设为mid)
至少一个士兵的处理,可以直接对于各个点需要几个士兵向汇点连边,即边界点需要二分数值的士兵,而其他需要1个士兵;也可以一开始就把各点的士兵数都-1.
#include<iostream> #include<queue> #include<cmath> #include<cstring> #include<vector> #include<algorithm> #include<string> using namespace std; const int maxn = 250, INF = 10000000; //需改 int a[maxn], mark[maxn]; string map[maxn]; struct Edge { int from, to, cap, flow; Edge(int u, int v, int c, int f) :from(u), to(v), cap(c), flow(f) {} }; struct EdmondsKarp { int n, m; vector<Edge> edges; vector<int> G[maxn]; int a[maxn]; int p[maxn]; void init(int n) { this->n = n; for (int i = 0; i < n; i++) { G[i].clear(); } edges.clear(); memset(p, 0, sizeof(p));//不需要? } void AddEdge(int u, int v, int c) { edges.push_back(Edge(u, v, c, 0)); edges.push_back(Edge(v, u, 0, 0)); m = edges.size(); G[u].push_back(m - 2); G[v].push_back(m - 1); } int MaxFlow(int s, int t) { int flow = 0; for (;;) { memset(a, 0, sizeof(a)); queue<int> Q; Q.push(s); a[s] = INF; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if (!a[e.to] && e.cap > e.flow) { a[e.to] = min(a[u], e.cap - e.flow); Q.push(e.to); p[e.to] = G[u][i]; } } if (a[t]) break; } if (!a[t]) break; for (int i = t; i != s; i = edges[p[i]].from) { edges[p[i]].flow += a[t]; edges[p[i] ^ 1].flow -= a[t]; } flow += a[t]; } return flow; } }E; int Build(int val,int N) { /*for (int i = 1; i <= n; i++) //WA { if (a[i]) { E.AddEdge(0, i, a[i]); E.AddEdge(i, n + i, a[i]); } } int flag = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (map[i][j] == 'Y') { if (a[i] && a[j]) //我我交界 { E.AddEdge(i, n + j, INF);//1.无向2.容量INF/a[i] //E.AddEdge(n + i, 2 * n + 1, 1);//合并循环时一定注意,可能实际上并不能合并 if(!flag) mark[i] = 2; } else if (a[i] && !a[j]) //敌我交界 { mark[i] = 1; flag = 1; } } } } int ans = 0; //总容量 for (int i = 1; i <= n; i++) { if (mark[i] == 1) { E.AddEdge(n + i, 2 * n + 1, val); ans += val; } else if (mark[i] == 2) { E.AddEdge(n + i, 2 * n + 1, 1); ans++; } } return ans;*/ for (int i = 1; i <= N; i++) //AC { if (!a[i]) continue; E.AddEdge(0, i, a[i]); E.AddEdge(i, i + N, a[i]); for (int j = 1; j <= N; j++) if (map[i][j] == 'Y') { if (!a[j]) mark[i] = true; else E.AddEdge(i, j + N, INF); } } int ans = 0; for (int i = 1; i <= N; i++) if (mark[i]) { E.AddEdge(i + N, 2 * N + 1, val); ans += val; } else if (a[i]) { b9fd E.AddEdge(i + N, 2 * N + 1, 1); ans++; }; return ans; } void solve(int n) { int a, b, ans; int l = 0, r = 10010; while (l<r) { E.init(2 * n + 2); //注意初始化位置,由此影响到每次循环中,不止容量需要修改,整个图都要重建 int mid = (l + r) >> 1; a = Build(mid, n); b = E.MaxFlow(0, 2 * n + 1); if (a == b) { l = mid + 1; ans = mid; } else r = mid; } cout << ans << endl; } int main() { int T; cin >> T; while (T--) { memset(a, 0, sizeof(a)); memset(mark, 0, sizeof(mark)); for (int i = 0; i < maxn; i++) map[i].clear(); int n; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { cin >> map[i]; map[i] = '0' + map[i]; //为了编号从1开始//'0' + map[i] 不等于 map[i] + '0' } solve(n); } }
相关文章推荐
- UVa12264 Risk(最大流)
- uva 12264 (Risk) 二分 + 最大流
- 最大流,二分法,拆点法(士兵移动 uva 12264)
- Risk UVA - 12264 拆点法+最大流+二分 最少流量的节点流量尽量多。
- UVA 12264 Risk 二分最大流
- Risk UVA - 12264
- UVA - 12264 Risk (二分,网络流)
- UVA12264 二分最大流,注意pdf的样例是错的
- Uva-12264-Risk
- 8.6-244-uva714-Copying books-二分法-最大值尽量小
- uva 714 Copying Books(二分法求最大值最小化)
- UVA 12264 二分+最大流 基础题
- 网络流 UVA 12264 Risk
- uva 714 Copying Books(二分法求最大值最小化)
- 二维数组, 二分法查找数组元素下标 快速查找数组最大值 数组作为实参的问题
- Uva 11248 Frequency Hopping(最大流)
- UVa1161 Objective: Berlin(最大流)
- UVAlive--4529--Dangerous Tunnels(二分+拆点最大流)
- 例题1.22 最大子矩阵 City Game UVALive - 3029 扫描法
- uva753 A Plug for UNIX 网络流最大流