您的位置:首页 > 其它

uva 12264 (Risk) 二分 + 最大流

2018-02-06 11:37 609 查看
题意: 首先有n个点, 然后每个点都一个权值ai表示这个点上的士兵数量,如果ai为0则表示这个点是敌人的, 如果大于零则表示这个点是自己的并且有ai个士兵, 现在你只有一次机会可以移动所有的士兵, 注意 每个士兵只能移动一次, 并且只有两种选择, 要么不动, 要么移动到相邻的一个单位处的的自己的点上, 并且自己的点在移动完之后至少要存在一名士兵。 另外再输入一个n * n的字符矩阵, 如果s【i】【j】 为Y则i, j相邻, 并且 i, i永远为N; 好了, 现在要求出 当敌人的点的相邻自己点上的士兵都达到最大值的时候,
自己的电商的士兵数量最小的点的士兵数量是多少。

思路: 一个很nice的思路, 我也是看到了题解之后才恍然大悟, 首先你要求最大的最小, 第一反应肯定是二分。但是之后又会想 二分怎么分上下呢。。。这就很尴尬了。 我开始看这个题的时候根本没想到网络流。。。。。然后想了一段时间也没想出来就看了题解, 二分是对了, 分上下界的时候要用的网络流, 建图的思想就是把这些点分开, 因为不是自己的点不能放士兵, 所以我们不管这些点, 然后我们把剩下自己的点分成和不是自己的点相邻的点和仅与自己的点相邻的点, 其次我们把每个点分成i和i', 首先i连源点流量为a[i],
然后i和i'连, 流量为a[i], i’是要连汇点的所以这样可以保证连完图之后士兵只走一次, 然后因为是自己的点, 所以每个点都可以给予相邻的自己点的流量, 假如i跟j相邻, i,j都是自己的点, 那么连i到j‘流量为a[i], 这很好理解, i点自己的士兵可以走到j点, 因为士兵只能走一次, 所以i只能给j自己点上的士兵, 之后连汇点就有区别了, 如果这个点和敌人的点相邻, 那么这个i’连汇点, 流量为二分出来的mid, 否则连1, 且mid的最小值为1, 这样应该好理解吧, 我们二分就是要二分最小的那个值,
然后跑最大流看看每次是不是满流, 这样建图之后我们就可以看到每次二分出来的最小值对这个网络有没有影响, 因为这样建图我们最后得出来的最大流是所有士兵的总和, 满流了, 那么就可行, 继续向上二分,反之向下。

代码:这里用的EK, 较慢, 因为忘了带dinic了。。。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
const int maxed = 300 + 10;
struct E
{
int from, to, cap, flow;
};
struct EK
{
int n, m;
vector<E> edg;
vector<int> G[maxed];
int a[maxed];
int p[maxed];

void init(int n)
{
for (int i = 1; i <= n; ++i)
G[i].clear();
edg.clear();
}

void add_(int from, int to, int cap)
{
edg.push_back((E){from, to, cap, 0});
edg.push_back((E){to, from, 0, 0});
m = edg.size();
G[from].push_back(m - 2);
G[to].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 x = Q.front(); Q.pop();
for (int i = 0; i < G[x].size(); ++i) {
E& e = edg[G[x][i]];
if (!a[e.to] && e.cap > e.flow) {
p[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);
Q.push(e.to);
}
}
if (a[t])
break;
}
if (!a[t])
break;
for (int u = t; u != s; u = edg[p[u]].from) {
edg[p[u]].flow += a[t];
edg[p[u] ^ 1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
};
char s[maxed][maxed];
int n, p[maxed];
int main()
{
bool slove(int mid);
int N;
scanf("%d", &N);
while (N--) {
scanf("%d", &n);
int max_ = 0, min_ = 1;
for (int i = 1; i <= n; ++i) {
scanf("%d", &p[i]);
max_ += p[i];
}
for (int i = 1; i <= n; ++i)
scanf("%s", s[i] + 1);
int answer = 1;
while (max_ >= min_) {
int mid = (max_ + min_) >> 1;
if (slove(mid)) {
min_ = mid + 1;
answer = max(answer, mid);
}
else
max_ = mid - 1;
}
printf("%d\n", answer);
}
return 0;
}
bool slove(int mid)
{
EK ek;
int sum = 0;
for (int i = 1; i <= n; ++i)
if (p[i]) {
ek.add_(0, i, p[i]);
ek.add_(i, i + n, p[i]);
bool flag = false;
for (int j = 1; j <= n; ++j)
if (s[i][j] == 'Y') {
if (!p[j])
flag = true;
else
ek.add_(i, j + n, p[i]);
}
if (flag) {
ek.add_(i + n, 2 * n + 1, mid);
sum += mid;
}
else {
ek.add_(i + n, 2 * n + 1, 1);
sum += 1;
}
}
return ek.Maxflow(0, 2 * n + 1) == sum ? true : false;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: