POJ - 1459 Power Network (裸最大流)
Time Limit: 2000MS | Memory Limit: 32768K | |
Total Submissions: 28550 | Accepted: 14796 |
Description
![](https://i-blog.csdnimg.cn/blog_migrate/1c1354d5a7c820146d570a7875b230b3.jpeg)
An example is in figure 1. The label x/y of power station u shows that p(u)=x and p max(u)=y. The label x/y of consumer u shows that c(u)=x and c max(u)=y. The label x/y of power transport line (u,v) shows that l(u,v)=x and l max(u,v)=y. The power consumed is Con=6. Notice that there are other possible states of the network but the value of Con cannot exceed 6.
Input
Output
Sample Input
2 1 1 2 (0,1)20 (1,0)10 (0)15 (1)20 7 2 3 13 (0,0)1 (0,1)2 (0,2)5 (1,0)1 (1,2)8 (2,3)1 (2,4)7 (3,5)2 (3,6)5 (4,2)7 (4,3)5 (4,5)1 (6,0)5 (0)5 (1)2 (3)2 (4)1 (5)4
Sample Output
15 6
Hint
Source
题目大意:一共有n个点,有np个生产点,有nc个消费点,有m条有向边,问最多消费者会购买多少单位的东西。接下来m个三元组,表示有从点u到点v最多能够运输w个单位的东西,再接下来np个二元组,表示生产点u能够生产w个单位的东西,同样,再接下来nc个二元组,表示消费点u能够买w个单位的东西。
思路:
1、首先对m条边,直接对从u到v建边。
2、然后对于np个生产点,用一个超级源点连接这些个生产点,其边权值为生产点最多能够生产的物品单位数量。接着对于nc个消费点,将其连入一个超级汇点,每条边权值为消费点能够购买的物品单位数量。
3、这时候源点S也有了,汇点T也有了,直接跑一遍最大流算法即可。
这题每个点有明确的分工, 有的只生产,有的只输出, 跟二分图那样似的, 所以这样只把入流跟源点连起来, 出流跟汇点连起来...有些图要拆点,一般指的是没有明确的入流出流, 中间管道的权值是点的权值,所以这样的要拆点,入点跟出点 形成一个“水管”, 容量就是这个点的权值 这题就是这样的 -〉戳我
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 105;
const int maxv = 2e4;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum;
int n, np, nc, m;
struct node
{
int v, w, next;
}edge[maxv+6*maxn];
void addEdge(int u, int v, int w)
{
edge[k].v = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k++;
edge[k].v = u;
edge[k].w = 0;
edge[k].next = head[v];
head[v] = k++;
}
int bfs()
{
memset(d, 0, sizeof(d));
d[s] = 1;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
if(u == t) return 1;
q.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == 0)
{
d[to] = d[u] + 1;
if(to == t) return 1;
q.push(to);
}
}
}
return 0;
}
int dfs(int u, int maxflow)
{
if(u == t) return maxflow;
int ret = 0;
for(int i = cur[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == d[u]+1)
{
int f = dfs(to, min(maxflow-ret, w));
edge[i].w -= f;
edge[i^1].w += f;
ret += f;
if(ret == maxflow) return ret;
}
}
return ret;
}
int Dinic()
{
int ans = 0;
while(bfs() == 1)
{
memcpy(cur, head, sizeof(head));
ans += dfs(s, INF);
}
return ans;
}
int main()
{
while(~scanf("%d%d%d%d", &n, &np, &nc, &m))
{
s = n+1, k = 0, t = n+2;
memset(head, -1, sizeof(head));
int x, y, z;
while(m--)
{
scanf(" (%d,%d)%d", &x, &y, &z);
addEdge(x, y, z);
}
// cout << 1 << endl;
while(np--)
{
scanf(" (%d)%d", &x, &z);
addEdge(s, x, z);
}
while(nc--)
{
scanf(" (%d)%d", &x, &z);
addEdge(x, t, z);
}
printf("%d\n", Dinic());
}
return 0;
}
51nod 1442
在某个国家有n个城市,他们通过m条无向的道路相连。每个城市有一支军队。第i个城市的军队有ai个士兵。现在士兵开始移动。每个士兵可以呆在原地,或者走到和他所在城市直接相邻的城市,即设每一条边长度为1的话,他离开之后不能距离原来城市大于1。
判断移动之后,能不能使得第i个城市恰好有bi个士兵。
单组测试数据。 第一行有两个整数n和m(1 ≤ n ≤ 100, 0 ≤ m ≤ 200)。 第二行包含n个整数 a1, a2, ..., an (0 ≤ ai ≤ 100)。 第三行包含n个整数b1, b2, ..., bn (0 ≤ bi ≤ 100)。 接下来m行,每行给出两个整数 p 和q (1 ≤ p, q ≤ n, p ≠ q),表示p和q之间有一条无向的道路。 每两个城市之间最多有一条无向的道路。
如果能够调整成功,输出YES,否则输出NO。
4 4 1 2 6 3 3 5 3 1 1 2 2 3 3 4 4 2
YES
题意:
给定n个顶点m条边的无向图,每个点上有若干名士兵,每名士兵只能留在原地或移动到相邻点上,给出移动后n个顶点的士兵人数,问是否存在一种移动方案.
分析:
建图,对i=1,2,...,n,源点S向标号i的点连容量为a[i]的边,标号i+n的点向汇点T连容量为b[i]的边,标号i的点向标号i+n的点连容量为INF的边
(表示留在原地),若原图中i,j两点间有边相连,则标号i的点向标号j+n的点连容量为INF的边(表示可以从i到j),标号j的点向标号i+n的点连容
量为INF的边(表示可以从j到i),跑一次从S到T的最大流,如果最大流既与所有a[i]之和相等,又与所有b[i]之和相等,则有解。
总结:
发现题目一旦像这种有一堆点的起始状态,然后是目标状态的,并且可以用路径来更改每个点的状态的。这种题用最大网络流做准没错。这题拆点,不是像之前做的那种, 点有权值,边也有权值,把点拆开,当边。这题是拆点代表这个点可以到自己, 到相邻的点,是连得相邻边的出边,这样就是只能到达相邻边了~
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 105;
const int maxv = 2e4;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum, a[maxn], b[maxn];
int n, m;
int init()
{
s = 0, t = n*2+1, k = 0;
memset(head, -1, sizeof(head));
}
struct node
{
int v, w, next;
}edge[maxv+6*maxn];
void addEdge(int u, int v, int w)
{
edge[k].v = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k++;
edge[k].v = u;
edge[k].w = 0;
edge[k].next = head[v];
head[v] = k++;
}
int bfs()
{
memset(d, 0, sizeof(d));
d[s] = 1;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
if(u == t) return 1;
q.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == 0)
{
d[to] = d[u] + 1;
if(to == t) return 1;
q.push(to);
}
}
}
return 0;
}
int dfs(int u, int maxflow)
{
if(u == t) return maxflow;
int ret = 0;
for(int i = cur[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == d[u]+1)
{
int f = dfs(to, min(maxflow-ret, w));
edge[i].w -= f;
edge[i^1].w += f;
ret += f;
if(ret == maxflow) return ret;
}
}
return ret;
}
int Dinic()
{
int ans = 0;
while(bfs() == 1)
{
memcpy(cur, head, sizeof(head));
ans += dfs(s, INF);
}
return ans;
}
int main()
{
int x, y, sum1 = 0, sum2 = 0;
scanf("%d%d", &n, &m);
init();
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), addEdge(s, i, a[i]), sum1 += a[i], addEdge(i, i+n, INF);
for(int i = 1; i <= n; i++) scanf("%d", &b[i]), addEdge(i+n, t, b[i]), sum2 += b[i];
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
addEdge(x, y+n, INF);
addEdge(y, x+n, INF);
}
if(sum1 != sum2)
puts("NO");
else
{
int tmp = Dinic();
if(tmp != sum1)
puts("NO");
else
puts("YES");
}
return 0;
}
51nod 1757 大灾变
第1行两个整数n(n<=2000)和m(m<=40)表示节点个数和洞口个数 接下来n-1行每行两个整数表示树上的每一条边 第n+1行m个整数表示所有洞口的编号,保证洞口是叶子节点
一个整数t表示让所有种族躲进地下避难所的最少时间
6 2 1 2 1 3 1 4 1 5 5 6 3 6
3
题解:
首先二分答案,可以看出这是个很裸的网络流模型,但是问题在于怎么建模
总结:以前做过几个奶牛的, 也是从一些东西要去另一些地方, 但是那个没有什么要求, 就是floyd一下, 然后二分答案, 把所有距离小于答案的都连上边, 这样就好了,这题有个要求, 到洞口只能一个一个进, 这样就比较有趣了, 做法就是把洞口拆点。
但是这个题卡时, 让我知道了网络流模板哪里可以改很多。。以后大量数据的时候,都可以用下面的方法
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 2e3 + 7;
const int maxv = 2e6;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum, k1, head1[maxv], book[maxn], a[maxn];
int n, m, dis[50][maxn];
void init(int x)
{
s = 0, t = n+m*x+1, k = 0;
for(int i = 0; i <= t; i++) head[i] = -1;
}
struct node
{
int v, w, next;
}edge[maxv];
struct node1
{
int v, next;
}v[maxv];
void addEdge(int u, int v, int w)
{
edge[k].v = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k++;
edge[k].v = u;
edge[k].w = 0;
edge[k].next = head[v];
head[v] = k++;
}
void addEdge1(int u, int to)
{
v[k1].v = to;
v[k1].next = head1[u];
head1[u] = k1++;
}
int Q[maxv];
int bfs()
{
for(int i = 0; i <= t; i++) d[i] = 0; //这里可以手动赋值
d[s] = 1;
int frnt = 0, rear = 0; //手写队列比stl快400ms
Q[rear++] = s;
while(frnt != rear)
{
int u = Q[frnt++];
if(u == t) return 1;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == 0)
{
d[to] = d[u] + 1;
if(to == t) return 1;
Q[rear++] = to;
}
}
}
return 0;
}
int dfs(int u, int maxflow)
{
if(u == t || !maxflow) return maxflow;
int ret = 0;
for(int& i = cur[u]; i != -1; i = edge[i].next) //这里!!!i前面那个&一定要加上。。差了好几s
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == d[u]+1)
{
int f = dfs(to, min(maxflow-ret, w));
edge[i].w -= f;
edge[i^1].w += f;
ret += f;
if(ret == maxflow) return ret;
}
}
return ret;
}
int Dinic()
{
int ans = 0;
while(bfs() == 1)
{
for(int i = 0; i <= t; i++) //这里。。。我用memcpy就t, 很慢很慢,一直t, 手动赋值就贼快。。
cur[i] = head[i];
ans += dfs(s, INF);
}
return ans;
}
void get_dist(int id, int x, int f)
{
dis[id][x] = dis[id][f]+1;
for(int i = head1[x]; i != -1; i = v[i].next)
{
int to = v[i].v;
if(to == f) continue;
get_dist(id, to, x);
}
}
int check(int x)
{
init(x);
for(int i = 1; i <= n; i++)
if(!book[i])
addEdge(s, i, 1);
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= x; j++)
addEdge(n+(i-1)*x+j, t, 1);
for(int j = 1; j < x; j++)
addEdge(n+(i-1)*x+j, n+(i-1)*x+j+1, INF);
}
for(int i = 1; i <= n; i++)
{
if(!book[i])
{
for(int j = 1; j <= m; j++)
{
if(dis[j][i] <= x)
addEdge(i, n+(j-1)*x+dis[j][i], 1);
}
}
}
int res = Dinic();
return res == n-m;
}
int main()
{
int x, y;
k1 = 0;
scanf("%d%d", &n, &m);
memset(head1, -1, sizeof(head1)); //这改成for也是对的
for(int i = 1; i < n; i++)
{
scanf("%d%d", &x, &y);
addEdge1(x, y);
addEdge1(y, x);
}
for(int i = 1; i <= m; i++)
{
scanf("%d", &a[i]);
book[a[i]] = 1;
}
for(int i = 1; i <= m; i++)
dis[i][0] = -1, get_dist(i, a[i], 0);
int l = 0, r = n+1, mid, ans = 0;
while(l <= r)
{
mid = (l+r)>>1;
if(check(mid))
ans = mid, r = mid - 1;
else
l = mid + 1;
}
printf("%d\n", ans);
return 0;
}
计蒜客-Our Journey of Dalian Ends 2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛(最小费用最大流)
题意:
给定若干个城市,出发点为大连,目的地为西安,但是要求中途必须经过上海,并且图中每个城市只能经过一次,给出m条路(双向道路),走第i条路需要wi代价,求所有满足要求的方案中花费的最小代价,如果没有满足的方案,输出-1。
思路:
相当于求从大连到上海加上西安到上海花费的代价总和最小,点只可走一次,所以进行拆点,所以是最小费用最大流,最小费用最大流有不少是用来解决在一定限制下取得最大最小利益的, 不相交,只能走几次这类就更明显了,每个点只能走一次,上海可以走两次,那就其余点都拆点,流量是1, 上海拆点流量是2.源点连到西安跟大连的入点,流量是1,费用是0, 上海连到源点,流量是2,费用是0,这样求的就是流量从西安大连出发到上海结束的最大流量,在这基础上求得最小费用。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 4e4 + 7;
const int maxe = 200005;
const int INF = 1e9;
int head[maxe], dis[maxn], path[maxn], pre[maxn], book[maxn] , n, m, s, t, k, sum, index;
map<string, int> id;
char s1[maxn], s2[maxn];
struct node
{
int v, w, f, next, cnt;
}edge[3000005];
void addEdge(int u, int v, int f, int w)
{
edge[k].v = v;
edge[k].w = w;
edge[k].f = f;
edge[k].cnt = k;
edge[k].next = head[u];
head[u] = k++;
edge[k].v = u;
edge[k].w = -w;
edge[k].f = 0;
edge[k].cnt = k;
edge[k].next = head[v];
head[v] = k++;
}
void init()
{
s = 0, t = n*4+1, k = 0, index = 1;
memset(head, -1, sizeof(head));
id.clear();
}
int spfa()
{
queue<int> q;
q.push(s);
memset(pre, -1, sizeof(pre));
memset(path, -1, sizeof(path));
for(int i = 1; i <= t; i++) dis[i] = INF;
dis[s] = 0;
memset(book, 0, sizeof(book));
book[s] = 1;
while(!q.empty())
{
int u = q.front();
q.pop();
book[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
int w = edge[i].w;
int f = edge[i].f;
if(f && dis[to] > dis[u] + w)
{
// cout << u << ' ' << to << endl;
dis[to] = dis[u] + w;
pre[to] = u;
path[to] = edge[i].cnt;
if(!book[to])
{
q.push(to);
book[to] = 1;
}
}
}
}
// cout << dis[t] << endl;
if(dis[t] == INF) return 0;
else return 1;
}
int Min_costflow()
{
int ans = 0;
int maxflow = 0;
while(spfa())
{
int minx = INF;
for(int i = t; i != s; i = pre[i])
{
minx = min(minx, edge[path[i]].f);
}
maxflow += minx;
ans += dis[t]*minx;
// cout << ans << endl;
for(int i = t; i != s; i = pre[i])
{
edge[path[i]].f -= minx;
edge[path[i]^1].f += minx;
}
}
if(maxflow != 2) ans = -1;
return ans;
}
void check(char *s)
{
if(id[s] == 0) id[s] = index++;
}
int main()
{
int _;
// freopen("in.txt","r",stdin);
cin >> _;
while(_--)
{
scanf("%d", &n);
init();
int x, sh, xa, dl;
for(int i = 1; i <= n; i++)
{
scanf(" %s", s1);
check(s1);
scanf(" %s", s2);
check(s2);
scanf("%d", &x);
if(strcmp(s1, "Shanghai") == 0)
sh = id[s1];
if(strcmp(s2, "Shanghai") == 0)
sh = id[s2];
if(strcmp(s1, "Xian") == 0)
xa = id[s1];
if(strcmp(s2, "Xian") == 0)
xa = id[s2];
if(strcmp(s1, "Dalian") == 0)
dl = id[s1];
if(strcmp(s2, "Dalian") == 0)
dl = id[s2];
addEdge(id[s1]+n*2, id[s2], 1, x);
addEdge(id[s2]+n*2, id[s1], 1, x);
}
for(int i = 1; i < index; i++)
{
if(i == sh) addEdge(i , i+n*2, 2, 0);
else
addEdge(i, i+n*2, 1, 0);
}
addEdge(s, dl, 1, 0);
addEdge(s, xa, 1, 0);
addEdge(sh+n*2, t, 2, 0);
int ans = Min_costflow();
printf("%d\n", ans);
}
return 0;
}
/*
3
2
Dalian Shanghai 3
Shanghai Xian 4
5
Dalian Shanghai 7
Shanghai Nanjing 1
Dalian Nanjing 3
Nanjing Xian 5
Shanghai Xian 8
3
Dalian Nanjing 6
Shanghai Nanjing 7
Nanjing Xian 8
*/