算法11

【网络流之费用流专题】解题报告

 

  (说明:这个专题中有很多题目也可以运用KM匹配算法。)

 

HDU 1533 Going Home

  入门题,典型二分图最优权值匹配。

  建图:

  1. 超级源点与人连边,流量为1,花费为0;

  2. 房子与超级汇点连边,流量为1,花费为0;

  3. 每个人与每个房子连边,流量为1,花费为距离。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 505
#define MAXM 4000005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;
char g[105][105];
vector<pii> vm, vh;

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
int flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}

void init()
{
NE = 0;
memset(index, -1, sizeof index);
}

int main()
{
while(RII(n, m) != EOF)
{
if(n == 0 && m == 0) break;
vm.clear(); vh.clear();
rep(i,0,n-1)
{
scanf("%s", g[i]);
rep(j,0,m-1)
if(g[i][j] == 'm')
vm.PB(MP(i,j));
else if(g[i][j] == 'H')
vh.PB(MP(i,j));
}
int s = 0, t = vm.size() + vh.size() + 1;
int m = vm.size();
init();
rep(i,0,m-1)
{
add_edge(0, 1+i, 1, 0);
rep(j,0,vh.size()-1)
add_edge(1+i, 1+m+j, 1, abs(vm[i].F - vh[j].F)+abs(vm[i].S-vh[j].S));
}
rep(j,0,vh.size()-1)
add_edge(1+m+j, t, 1, 0);
printf("%d\n", MinCostMaxFlow(s,t));
}
}

 

HDU 3488 Tour

  基础题,求一个最小费用圈。

  其实求圈的话可以转换成求从起点到终点的两条不重复的最小费用流。那么就是超级源点到起点、终点到超级汇点的容量都是2,费用都是零,其他点之间连边容量为1,花费为距离,求一次最小费用最大流即可。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 505
#define MAXM 400005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

int n, m, k;
char g[105][105];
vector<pii> vm, vh;

#define typef int // type of flow
#define typec int // type of dis
const typef inff = 0x3f3f3f3f; // max of flow
const typec infc = 0x3f3f3f3f; // max of dis
struct network
{
int nv, ne, pnt[MAXM], nxt[MAXM];
int vis[MAXN], que[MAXN], head[MAXN], pv[MAXN], pe[MAXN];
typef flow, cap[MAXM];
typec cost, dis[MAXM], d[MAXN];
void add_edge(int u, int v, typef c, typec w)
{
pnt[ne] = v;
cap[ne] = c;
dis[ne] = +w;
nxt[ne] = head[u];
head[u] = (ne++);
pnt[ne] = u;
cap[ne] = 0;
dis[ne] = -w;
nxt[ne] = head[v];
head[v] = (ne++);
}
int mincost(int src, int sink)
{
int i, k, f, r;
typef mxf;
for(flow = 0, cost = 0; ; )
{
memset(pv, -1, sizeof(pv));
memset(vis, 0, sizeof(vis));
for(i = 0; i < nv; ++i) d[i] = infc;
d[src] = 0;
pv[src] = src;
vis[src] = 1;
for(f = 0, r = 1, que[0] = src; r != f; )
{
i = que[f++];
vis[i] = 0;
if(MAXN == f) f = 0;
for(k = head[i]; k != -1; k = nxt[k])
if(cap[k] && dis[k]+d[i] < d[pnt[k]])
{
d[pnt[k]] = dis[k] + d[i];
if(0 == vis[pnt[k]])
{
vis[pnt[k]] = 1;
que[r++] = pnt[k];
if(MAXN == r) r = 0;
}
pv[pnt[k]]=i;
pe[pnt[k]]=k;
}
}
if(-1 == pv[sink]) break;
for(k = sink, mxf = inff; k != src; k = pv[k])
if(cap[pe[k]] < mxf) mxf = cap[pe[k]];
flow += mxf;
cost += d[sink] * mxf;
for(k = sink; k != src; k = pv[k])
{
cap[pe[k]] -= mxf;
cap[pe[k] ^ 1] += mxf;
}
}
return cost;
}

void build(int s, int t)
{
int a, b, c;
nv = 2 + 2*n;
ne = 0;
memset(head, -1, sizeof(head));
rep(i,1,m)
{
RIII(a,b,c);
add_edge(a, b+n, 1, c);
}
rep(i,1,n) {add_edge(s, i, 1, 0); add_edge(i+n, t, 1, 0);}
}
} G;

int main()
{
int t;
RI(t);
while(t--)
{
RII(n, m);
int s = 0, t = 2*n+1;
G.build(s, t);
printf("%d\n", G.mincost(s,t));
}
}

 

HDU 3435 A new Graph Game

  题目求在保证图的汉密尔顿环约束下删除尽量多、花费尽量大的边,剩余的最小边权和(最小费用)。

  对每个点进行拆点对于每一点i,s->i , i'->t的边容量为1,花费为0,如果 i 和 j 之间有边相连(双向边哦),则i->j', j->i',容量为1,花费为weight。

  当然,这题要判断是否是满流。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 2005
#define MAXM 400005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t, int &flow)
{
int cost = 0;
flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

void build(int s, int t)
{
int a, b, c;
init();
rep(i,1,m)
{
RIII(a,b,c);
add_edge(a, b+n, 1, c);
add_edge(b, a+n, 1, c);
}
rep(i,1,n) {add_edge(s, i, 1, 0); add_edge(i+n, t, 1, 0);}
}

int main()
{
int cas, flow;
RI(cas);
rep(cc, 1, cas)
{
RII(n, m);
int s = 0, t = 2*n+1;
build(s, t);
printf("Case %d: ", cc);
int ans = MinCostMaxFlow(s,t,flow);
if(flow != n)
{
puts("NO");
continue;
}
printf("%d\n", ans);
}
}

 

HDU 1853 Cyclic Tour

  和上一题差不多,只不过边是单向的。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 505
#define MAXM 400005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t, int &flow)
{
int cost = 0;
flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

void build(int s, int t)
{
int a, b, c;
init();
rep(i,1,m)
{
RIII(a,b,c);
add_edge(a, b+n, 1, c);
}
rep(i,1,n) {add_edge(s, i, 1, 0); add_edge(i+n, t, 1, 0);}
}

int main()
{
int flow;
while(RII(n, m) != EOF)
{
int s = 0, t = 2*n+1;
build(s, t);
int ans = MinCostMaxFlow(s,t,flow);
if(flow != n)
{
puts("-1");
continue;
}
printf("%d\n", ans);
}
}

 

HDU 2686 Matrix   HDU 3376 Matrix Again

  求最小费用圈,添加的花费要变为负,再求最小费用,不然是求不出的,最后求得最小费用最大流后再变为正就可以了。

  (HDU 3376貌似卡EK算法,我没试过,我两题交的同样的代码用SAP算法AC的)

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 50005
#define MAXM 5000005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;
int a[MAXN];

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
int flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

void build()
{
k = 0;
rep(i,0,n-1)
rep(j,0,n-1)
RI(a[k++]);

init();
rep(i,0,n*n-1)
{
if(i == 0)
add_edge(0, 0+n*n, 2, 0);
else if(i == n*n-1)
add_edge(n*n-1, 2*n*n-1, 2, 0);
else
add_edge(i, i+n*n, 1, -a[i]); // 权值变负来求最小费用
}
rep(i,0,n-1)
{
rep(j,0,n-1)
{
if((i+1) < n)
add_edge(n*n + i*n+j, (i+1)*n+j, 1, 0);
if((j+1) < n)
add_edge(n*n + i*n+j, i*n+j+1, 1, 0);
}
}
}

int main()
{
while(RI(n) != EOF)
{
build();
printf("%d\n", a[0]+a[n*n-1]-MinCostMaxFlow(n*n, n*n-1));
}
return 0;
}

 

HDU 3667 Transportation

  非常不错的一道题,由于有平方项,直接相加无法构成线性关系,怎么办?那么我们只要想办法把平方项转化为线性的(多个数相加),利用关系式:

  n2 = 1 + 3 + 5 + 7 + ... + (2*n-1)

  那么对于一条最大承载量为Ci的边,拆成Ci条容量为1,花费为ai*(2*i+1) (0<= i < Ci)。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 505
#define MAXM 400005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
if(flow != k) return -1;
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

void build(int s, int t)
{
int u, v, a, c;
init();
add_edge(s, 1, k, 0);
rep(i,1,m)
{
scanf("%d%d%d%d", &u, &v, &a, &c);
rep(j,0,c-1)
add_edge(u, v, 1, a*(2*j+1));
}
}

int main()
{
while(RIII(n,m,k) != EOF)
{
int s = 0, t = n;
build(s, t);
printf("%d\n", MinCostMaxFlow(s, t));
}
return 0;
}

 

My Brute  My Brute

  第一问求最小费用,比较简单。而第二问要求在尽量不改变原顺序的基础上求出一个比率。

  乍一看,还不会做,后来还是套用了KM算法的思想:很明显,这是一个二分图,分为两个集合S和T,KM算法的思想是从S的点 i 出发,先找与i相连的一条权值最大边,找不到最大边就找次大边,不断寻找增广链直到所有点都匹配。那么对于点i,如果该点出发的边中,i->i' 与 i->j' 的权值相等,我们需要优先选择i->i'以保持最少的顺序改变,做法是在i->i'加上1使得在相等的情况下i->i'显大一些就会优先选择到,但是加上1之后会改变最终的花费,那么,如果总点数为n,那么我们乘以一个比n大的常数k,因为最后总的匹配边数最大为n,乘以k之后我们在原i->i'中加1的操作在最后除以k时就解决掉这个影响,那么假如最后我们得到的最小费用最大流为ans,那么ans/k就是第一问,ans%k就是不改变的边的数量(相似度)。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define N 205
#define M 4000005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[M];

int head[N];
int NE;
int pre[N], pos[N];
int dis[N];
bool vis[N];
int n, m, k;
queue<int> que;
int V[N], P[N], H[N], A[N], B[N];

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = head[from]; head[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = head[to]; head[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
memset(dis, 0x3f, sizeof dis);
que.push(s);
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(!que.empty())
{
int cur = que.front(); que.pop();
vis[cur] = 0;
for(int i = head[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que.push(adj);
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
int flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}

void init()
{
NE = 0;
memset(head, -1, sizeof head);
}

void read()
{
rep(i,1,n) RI(V[i]);
rep(i,1,n) RI(H[i]);
rep(i,1,n) RI(P[i]);
rep(i,1,n) RI(A[i]);
rep(i,1,n) RI(B[i]);
}

bool beat(int i, int j)
{
int r1 = H[i]/B[j] + (H[i]%B[j] != 0);
int r2 = P[j]/A[i] + (P[j]%A[i] != 0);
return r1 >= r2;
}

void build(int s, int t)
{
init();
rep(i,1,n)
{
add_edge(s, i, 1, 0);
add_edge(i+n, t, 1, 0);
}
rep(i,1,n)
{
rep(j,1,n)
{
if(i == j)
{
if(beat(i,j))
add_edge(i, j+n, 1, -V[i]*100-1); // 求最小费用要反号,同时+1边-1
else
add_edge(i, j+n, 1, V[i]*100-1);
}
else
{
if(beat(i,j))
add_edge(i, j+n, 1, -V[i]*100);
else
add_edge(i, j+n, 1, V[i]*100);
}
}
}
}

int main()
{
while(RI(n) != EOF && n)
{
read();
int s = 0, t = 2*n+1;
build(s, t);
int ans = -MinCostMaxFlow(s, t);
if(ans <= 0)
puts("Oh, I lose my dear seaco!");
else
printf("%d %.3f%%\n", ans/100, 100*(ans%100)/(n*1.0));
}
return 0;
}

 

POJ 2135 Farm Tour

  求最小费用圈,注意边要双向。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 5005
#define MAXM 4000005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
int flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

void build(int s, int t)
{
int a, b, c;
init();
add_edge(s, 1, 2, 0);
add_edge(n, t, 2, 0);
rep(i,1,m)
{
RIII(a,b,c);
add_edge(a, b, 1, c);
add_edge(b, a, 1, c);
}
}

int main()
{
while(RII(n, m) != EOF)
{
int s = 0, t = n+1;
build(s, t);
printf("%d\n", MinCostMaxFlow(s,t));
}
}

 

POJ 2516 Minimum Cost

  简单题,求k次最小费用最大流即可,每次要判断一下是否是满流。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 205
#define MAXM 500005
#define MAXK 55
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

struct Node
{
int u, v, c;
}a[MAXN];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;
int S[MAXN][MAXK], C[MAXN][MAXK];
int CC[MAXK][MAXN][MAXN];

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t, int &flow)
{
int cost = 0;
flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

int main()
{
while(RIII(n, m, k) != EOF)
{
if(n == 0 && m == 0 && k == 0)
break;
rep(i,1,n)
rep(j,1,k)
RI(C[i][j]);
rep(i,1,m)
rep(j,1,k)
RI(S[i][j]);
rep(i,1,k)
rep(p,1,n)
rep(q,1,m)
RI(CC[i][p][q]);
int sum = 0, tot, flow;
int s = 0, t = n + m + 1;
bool flag = true;
rep(i,1,k)
{
init();
tot = 0;
rep(j,1,n)
{
add_edge(s, j, C[j][i], 0);
tot += C[j][i];
}
rep(j,1,m)
add_edge(n+j, t, S[j][i], 0);
rep(p,1,n)
rep(q,1,m)
add_edge(p, n+q, inf, CC[i][p][q]);

int ans = MinCostMaxFlow(s, t, flow);
if(flow != tot) flag = false;
if(!flag) break;
sum += ans;
}
if(!flag)
puts("-1");
else
printf("%d\n", sum);
}
return 0;
}

 

POJ 3422 Kaka's Matrix Travels

  直接说怎么建图了,因为可以走k次,超级源点与左上角点连边,容量为m,花费为0,右下角点与超级汇点连边,同样容量为m,花费为0,然后对于每个格子i拆点,i->i'之间连两条边,一条容量为1,花费为该格子的值(后面求最小费用要变负),另一条容量为m-1,花费为0,表示这个格子走过之后还可以继续走。最后是每个格子和棋右边和下边相邻的格子连一条边i'->j,容量为m,花费为0。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 50005
#define MAXM 5000005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;
int a[MAXN];

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
int flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0;
memset(index, -1, sizeof(index));
}

void build(int s, int t)
{
k = 0;
rep(i,0,n-1)
rep(j,0,n-1)
RI(a[k++]);
init();
rep(i,0,n*n-1)
{
add_edge(i, i+n*n, 1, -a[i]);
add_edge(i, i+n*n, m-1, 0);
}
rep(i,0,n-1)
{
rep(j,0,n-1)
{
if((i+1) < n)
add_edge(n*n + i*n+j, (i+1)*n+j, m, 0);
if((j+1) < n)
add_edge(n*n + i*n+j, i*n+j+1, m, 0);
}
}
add_edge(s, 0, m, 0);
add_edge(2*n*n-1, t, m, 0);
}

int main()
{
while(RII(n,m) != EOF)
{
int s = 2*n*n;
int t = s+1;
build(s, t);
printf("%d\n",-MinCostMaxFlow(s, t));
}
return 0;
}

 

POJ 3680 Intervals

  好题。

  首先对区间端点进行离散化,设L为所有区间左端点最小值,R为所有区间右端点最大值,我们需要连边 i -> i+1 ,容量为K,花费为0。然后对于题目给出的每个区间(i,j),连一条边i->j,容量为1,花费为给定权值。超级源点与L连边,容量为K,花费为0,R与超级汇点连边,同样的容量为K,花费为0。

  View Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
using namespace std;
#define MP make_pair
#define PB push_back
#define F first
#define S second
#define pii pair<int,int>
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define inf (0x3f3f3f3f)
#define eps 1e-6
#define MAXN 505
#define MAXM 500005
#define MODN 1000000007
#define RI(x) scanf("%d", &x)
#define RII(x,y) scanf("%d%d", &x, &y)
#define RIII(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define debug puts("reach here");
typedef long long LL;

struct Edge
{
int to;
int vol;
int cost;
int next;
}E[MAXM];

struct Node
{
int u, v, c;
}a[MAXN];

int index[MAXN];
int NE;
int pre[MAXN], pos[MAXN];
int dis[MAXN], que[MAXM];
bool vis[MAXN];
int n, m, k;
bool h[MAXM];
int id[MAXM];

void add_edge(int from, int to, int vol, int cost)
{
E[NE].to = to; E[NE].vol = vol; E[NE].cost = cost; E[NE].next = index[from]; index[from] = NE++;
E[NE].to = from; E[NE].vol = 0; E[NE].cost = -cost; E[NE].next = index[to]; index[to] = NE++;
}

bool spfa(int s, int t)
{
memset(pre, -1, sizeof(pre));
memset(vis, 0, sizeof(vis));
int head, tail; head = tail = 0;
memset(dis, 0x3f, sizeof dis);
que[tail++] = s;
pre[s] = s;
dis[s] = 0;
vis[s] = 1;
while(head != tail)
{
int cur = que[head++];
vis[cur] = 0;
for(int i = index[cur]; i != -1; i = E[i].next)
{
int adj = E[i].to;
if(E[i].vol > 0 && dis[cur] + E[i].cost < dis[adj])
{
dis[adj] = dis[cur] + E[i].cost;
pre[adj] = cur;
pos[adj] = i;
if(!vis[adj])
{
vis[adj] = 1;
que[tail++] = adj;
}
}
}
}
return pre[t] != -1;
}

int MinCostMaxFlow(int s, int t)
{
int cost = 0;
int flow = 0;
while(spfa(s, t))
{
int f = inf;
for(int i = t; i != s; i = pre[i])
if (E[pos[i]].vol < f) f = E[pos[i]].vol;
flow += f; cost += dis[t] * f;
for(int i = t; i != s; i = pre[i])
{
E[pos[i]].vol -= f;
E[pos[i] ^ 1].vol += f;
}
}
return cost;
}
void init()
{
NE = 0; clr(h); k = 0;
memset(index, -1, sizeof(index));
}

int main()
{
int cas;
RI(cas);
while(cas--)
{
RII(n, m);
init();
int l = MAXM, r = -1;
rep(i,1,n)
{
RIII(a[i].u, a[i].v, a[i].c);
h[a[i].u] = h[a[i].v] = true;
l = min(l, a[i].u);
r = max(r, a[i].v);
}
rep(i,l,r)
if(h[i])
id[i] = ++k;
int s = 0, t = k+1;
rep(i,1,k-1)
add_edge(i,i+1,m,0);
rep(i,1,n)
add_edge(id[a[i].u], id[a[i].v], 1, -a[i].c);

add_edge(s, 1, m, 0);
add_edge(k, t, m, 0);
printf("%d\n", -MinCostMaxFlow(s, t));
}
return 0;
}

 

 

 

 

 
 
分类:  图论题集

你可能感兴趣的:(算法)