您的位置:首页 > 理论基础 > 计算机网络

hdu 2732 网络流,建图

2017-10-02 11:10 239 查看
题目大意:

给了n和d,表示有n行的图,(列数没给),对于每一个人都可以最长跳d的距离,对于每一个柱子有一个限定条件,只能从这里跳出去多少次。给了两张图,一张表示柱子的限定条件,一张给了现在那些柱子上有人。问有几个人不能跳到图的外面。

思路:

最大流建图。将图的外面抽象为一个汇点t,对于图中本来就可以跳出去的点,就可以连一条边到t了。对于可以跳到图中别的点的,就可以从这个点连一条边过去。比较直观的想法就是这些边的容量都设为这个柱子限定的次数,最后跑最大流就好了。但是如果这个柱子可以跳到旁边很多的点上,每一个边的容量都是这个限定次数的话,等于限定的次数被放大了。那么就考虑拆点,将每个点拆成点1,点2,1->2连一条边容量为限定的次数。对于能跳到别的边,就从2连一条边到外面的点,容量为INF。

步骤:

拆点

1.将能够直接跳出去的点,拆出的第一个点连到t,容量为限定的次数。

2.不能直接跳出去的,将点1连到点2,容量为限定的次数。

3.对于能跳到别的点的,将这个点2连到别的点的点1,容量为INF。

4.对于柱子上有人的,从s连一条边到该点的点1,容量为1。

跑最大流。

题解转自:http://blog.csdn.net/liujc_/article/details/51179046

代码:

#include <bits/stdc++.h>
using namespace std;

const int INF  = 0x3f3f3f3f;
const int maxn = 808;
const int Mod  = 1e9 + 7;

#define ll       long long
#define mem(x,y) memset(x,y,sizeof(x))
#define IO       ios_base::sync_with_stdio(0), cin.tie(0);

inline ll gcd(ll a, ll b) {return a % b == 0 ? b : gcd(b, a % b);}
inline ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
inline ll quick_pow(ll x, int k) {ll ans = 1; while (k) { if (k & 1) ans = (ans * x) % Mod; x = x * x % Mod;  k >>= 1; } return ans;}

int dep[maxn];

struct Node {
int  v, w, re_id;
Node() {};
Node(int a, int b, int c) {
v = a, w = b, re_id = c;
}
};
vector<Node> node[maxn];
void addEdge(int u, int v, int w) {
node[u].push_back(Node(v, w, node[v].size()));
node[v].push_back(Node(u, 0, node[u].size() - 1));
}

int bfs(int s, int t) {
queue<int> Q;
mem(dep, -1);
dep[s] = 0;
Q.push(s);
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = 0; i < node[u].size(); i++) {
int v = node[u][i].v;
if (node[u][i].w > 0 && dep[v] == -1) {
dep[v] = dep[u] + 1;
Q.push(v);
}
}
}
return dep[t] != -1;
}
int  dfs(int s, int t, int f) {
if (s == t || f == 0) return f;
int sumf = 0;
for (int i = 0; i < node[s].size(); i++) {
int v = node[s][i].v;
if (node[s][i].w > 0 && dep[v] == dep[s] + 1) {
int tmp = dfs(v, t, min(f, node[s][i].w));
if (tmp > 0) {
node[s][i].w -= tmp;
node[v][node[s][i].re_id].w += tmp;
sumf += tmp, f -= tmp;
}
}
}
if (sumf == 0) dep[s] = -1;
return sumf;
}

int dinic(int s, int t) {
int ans = 0;
while (bfs(s, t))  ans += dfs(s, t, INF);
return ans;
}

char s1[30][30], s2[30][30];
int xx[4] = {1, -1, 0, 0}, yy[4] = {0, 0, 1, -1}, line, len, d;
bool canJumpAway(int x, int y) {
for (int i = 1; i <= 4; i++) {
int a = x + xx[i - 1] * d, b = y + yy[i - 1] * d;
if (a <= 0 || a > line || b <= 0 || b > len) return 1;
}
return 0;
}
int main() {
int tt, icase = 1, s = 0, t = 801;
cin >> tt;
while (tt--) {
for (int i = 0; i < maxn; i++)node[i].clear();
cin >> line >> d;

for (int i = 1; i <= line; i++)scanf("%s", s1[i]);
for (int i = 1; i <= line; i++) scanf("%s", s2[i]);
len = strlen(s1[1]);

int sum = 0;
for (int i = 1; i <= line; i++) {
for (int j = 0; j < len; j++) {
int x = i, y = j + 1;
int a1 = (x - 1) * len + y, a2 = a1 + 400;

if (s2[i][j] == 'L') { // 对于柱子上有人的,从s连一条边到该点的点1,容量为1
sum++;
addEdge(s, a1, 1);
}

if (s1[i][j] == '0') continue;
if (canJumpAway(x, y))addEdge(a1, t, s1[i][j] - '0');//将能够直接跳出去的点,拆出的第一个点连到t,容量为限定的次数
else {
addEdge(a1, a2, s1[i][j] - '0'); // 不能直接跳出去的,将点1连到点2,容量为限定的次数。
for (int k = -d; k <= d; k++)
for (int q = -d; q <= d; q++) {
if (k == 0 && q == 0) continue;
int a = x + k, b = y + q;
if (k * k + q * q <= d * d && s1[a][b - 1] != '0')
addEdge(a2, (a - 1)*len + b, INF); // 对于能跳到别的点的,将这个点2连到别的点的点1,容量为INF
}
}

}
}

int ans = sum - dinic(s, t);
if (ans == 0) printf("Case #%d: no lizard was left behind.\n", icase++);
else if (ans == 1) printf("Case #%d: 1 lizard was left behind.\n", icase++);
else printf("Case #%d: %d lizards were left behind.\n", icase++, ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: