标签:bzoj sdoi2010 费用流 edmondskarp 拆点
题目大意:宇宙空间中进行了一次竞速大赛。有两种飞行方式,第一种是通过正常的道路,但是只能从标号小的飞到标号大的地方;第二种是直接过去,但是需要花费固定的时间。问正好遍历一次所有的点最少需要的多少时间。
思路:费用流。把每个点拆点,S到每个点的起点连费用0的边,向每个终点连费用为固定费用的边,图中原有的边从一个的起点连到另一个点的终点。然后每个点的终点向T连边。跑最小费用最大流就是最后的答案。
CODE:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXP 1700
#define MAX 4000000
#define S 0
#define T (points << 1|1)
#define INF 0x3f3f3f3f
using namespace std;
int points,edges;
int head[MAXP],total = 1;
int next[MAX],aim[MAX],cost[MAX],flow[MAX];
int src[MAXP];
int f[MAXP],p[MAXP],from[MAXP];
bool v[MAXP];
inline void Add(int x,int y,int f,int c)
{
next[++total] = head[x];
aim[total] = y;
flow[total] = f;
cost[total] = c;
head[x] = total;
}
inline void Insert(int x,int y,int f,int c)
{
Add(x,y,f,c);
Add(y,x,0,-c);
}
inline bool SPFA()
{
static queue<int> q;
while(!q.empty()) q.pop();
q.push(S);
memset(f,0x3f,sizeof(f));
memset(v,false,sizeof(v));
f[S] = 0;
while(!q.empty()) {
int x = q.front(); q.pop();
v[x] = false;
for(int i = head[x]; i; i = next[i])
if(f[aim[i]] > f[x] + cost[i] && flow[i] > 0) {
f[aim[i]] = f[x] + cost[i];
if(!v[aim[i]]) {
v[aim[i]] = true;
q.push(aim[i]);
}
from[aim[i]] = x;
p[aim[i]] = i;
}
}
return f[T] != INF;
}
long long EdmondsKarp()
{
long long re = 0;
while(SPFA()) {
int remain_flow = INF;
for(int i = T; i != S; i = from[i])
remain_flow = min(remain_flow,flow[p[i]]);
for(int i = T; i != S; i = from[i]) {
flow[p[i]] -= remain_flow;
flow[p[i]^1] += remain_flow;
}
re += (long long)remain_flow * f[T];
}
return re;
}
int main()
{
cin >> points >> edges;
for(int x,i = 1; i <= points; ++i) {
scanf("%d",&src[i]);
Insert(0,i,1,0);
Insert(0,i + points,1,src[i]);
Insert(i + points,T,1,0);
}
for(int x,y,z,i = 1; i <= edges; ++i) {
scanf("%d%d%d",&x,&y,&z);
if(x > y) swap(x,y);
Insert(x,y + points,1,z);
}
cout << EdmondsKarp() << endl;
return 0;
}
标签:bzoj sdoi2010 费用流 edmondskarp 拆点
原文地址:http://blog.csdn.net/jiangyuze831/article/details/40504279