链接: http://poj.org/problem?id=3281
题意: 有n只牛,F种食物,D种饮料,每只牛有自己喜欢吃喝的食物饮料,每种牛只能吃喝一种食物饮料,每种食物饮料也只能被一只牛吃喝,现在要计算出满足牛的需求的最大是多少?
思路: 左边放食物,右边放饮料,中间放牛,对于每个牛进行拆点,i和i+100,S向食物点连边,边权为1 ,食物点向牛点连边边权为1 ,附加点向饮料点连边边权为1 ,饮料点向T点连边,边权为1 ,那么对于牛点向 拆出来的点建一条边权为1 的边,不然的话,只能保证每种食物饮料只能被一只牛吃喝,并不能保证每只牛只吃喝一种食物。
例如最简单的例子:
1 2 2
2 2 1 2 1 2
代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N =505;
const int inf =0x3f3f3f3f;
struct Edge
{
int from,to,cap,flow;
Edge(int u,int v,int c,int f):from(f),to(v),cap(c),flow(f){}
};
struct Dinic
{
int n,m,s,t;
vector< Edge >edges;
vector< int >G[N];
bool vis[N];
int d[N];
int cur[N];
void init()
{
for (int i=0; i<N; i++)
G[i].clear();
edges.clear();
}
void addedge(int from,int to,int cap)
{
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool bfs()
{
memset(vis,0,sizeof(vis));
queue<int >q;
for (int i=0; i<N; i++) d[i] = inf;
q.push(s); d[s]=0;vis[s]=1;
while(!q.empty()){
int x=q.front(); q.pop();
for(int i=0;i<G[x].size();i++){
Edge e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}
int dfs(int x,int a)
{
if(x==t||a==0) return a;
int flow=0,f;
for(int &i=cur[x];i<G[x].size();i++){
Edge &e=edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0) break;
}
}
return flow;
}
int maxflow(int s,int t)
{
this->s=s; this->t=t;
int flow=0;
while(bfs()){
memset(cur,0,sizeof(cur));
flow+=dfs(s,inf);
}
return flow;
}
}a;
int f,d;
int n,m;
int F,D;
int main()
{
scanf("%d %d %d",&n,&F,&D);
a.init();
int S,T;
T=501;
S=0;
for(int i=1;i<=F;i++){
a.addedge(S,i,1);
}
for(int i=1;i<=D;i++){
a.addedge(300+i,T,1);
}
for(int i=1;i<=n;i++){
scanf("%d %d",&f,&d);
int x;
for(int j=1;j<=f;j++){
scanf("%d",&x);
a.addedge(x,i+100,1);
}
for(int j=1;j<=d;j++){
scanf("%d",&x);
a.addedge(200+i,300+x,1);
}
a.addedge(100+i,200+i,1);
}
int ans=a.maxflow(S,T);
printf("%d\n",ans);
return 0;
}
/*
1 2 2
2 2 1 2 1 2
*/
uva 1658
题意: 求1点到n点的两条不相交的路径,并且要求路径长度最小。
思路: 因为两条路径不相交,所以除了1点和n点,其他所有的点和边只能走一次。但是如果我们仅仅对边限制,是不能保证点只走一次的,所以要按上边的方法,对点进行拆点。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf =0x3f3f3f3f;
const int N =2005;
struct Edge
{
int u,v;
int cap,flow;
int cost;
};
struct MCMF
{
int n,m,s,t;
vector<Edge>edges;
vector<int >G[N];
int inq[N];
int d[N];
int p[N];
int a[N];
void init(int n)
{
this->n=n;
for(int i=0; i<=n+2; i++)
{
G[i].clear();
}
edges.clear();
}
void adde(int u,int v,int cap,int cost)
{
edges.push_back((Edge){u,v,cap,0,cost});
edges.push_back((Edge){v,u,0,0,-cost});
m=edges.size();
G[u].push_back(m-2);
G[v].push_back(m-1);
}
bool spfa(int s,int t,int &flow,int &cost)
{
for(int i=0; i<=n+2; i++)d[i]=inf;
memset(inq,0,sizeof(inq));
d[s]=0;
inq[s]=1;
p[s]=0;
a[s]=inf;
queue<int >q;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
inq[u]=0;
for(int i=0; i<G[u].size(); i++)
{
Edge &e=edges[G[u][i]];
if(e.cap>e.flow&&d[e.v]>d[u]+e.cost)
{
d[e.v]=d[u]+e.cost;
p[e.v]=G[u][i];
a[e.v]=min(a[u],e.cap-e.flow);
if(!inq[e.v])
{
q.push(e.v);
inq[e.v]=1;
}
}
}
}
if(d[t]==inf) return 0;
flow+=a[t];
cost+=d[t]*a[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].u;
}
return 1;
}
void mincost_maxflow(int s,int t,int& flow,int & cost)
{
flow=0;
cost=0;
while(spfa(s,t,flow,cost));
}
} a;
int n,m;
int main()
{
int u,v,w;
while(scanf("%d %d",&n,&m)!=EOF)
{
a.init(2*n+2);
int S=1;
int T=2*n;
for(int i=1;i<=n;i++){
if(i==1||i==n){
a.adde(i,i+n,2,0);
}
else a.adde(i,i+n,1,0);
}
for(int i=1;i<=m;i++){
scanf("%d %d %d",&u,&v,&w);
a.adde(u+n,v,1,w);
}
int flow,cost;
flow=cost=0;
a.mincost_maxflow(S,T,flow,cost);
printf("%d\n",cost);
}
return 0;
}
/*
6 11
1 2 23
1 3 12
1 4 99
2 5 17
2 6 73
3 5 3
3 6 21
4 6 8
5 2 33
5 4 5
6 5 20
*/