1.hdu3987 Harry Potter and the Forbidden Forest
这道题目有双向边和单向边,破坏一条边的代价为Wi,求破坏的总代价最小的情况下,最少要破坏几条道路,使得城市0无法到达城市n-1
就是求0-(n-1)的最小割
这道题目有两个小技巧:
1.双向建边,不用建立四个,反向边就不用额外建立了
2.要保证一个变量a最小的情况下,使得另一个变量尽可能小,我们给a乘上一个权值X,这样每条边表示为,a的值为,b的值为
还能保证在求最大流的时候,还是以a为第一优先的,因为X很大,b的取值不会影响到a
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt;
const int maxn=1005;
const int maxm=1e5+5;
typedef long long ll;
struct edge
{
int to,nxt;
ll v;
}e[maxm<<1];
int head[maxn],dep[maxn];
void add(int x,int y,ll z)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
e[cnt].v=z;
head[x]=cnt;
}
ll ans=0;
bool bfs()
{
memset(dep,-1,sizeof(dep));
queue <int> q;
q.push(1);
dep[1]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(e[i].v>0 && dep[to]==-1)
{
dep[to]=dep[u]+1;
q.push(to);
}
}
}
return (dep[n]!=-1);
}
ll dfs(int u,ll flow)
{
if(u==n) return flow;
ll res=0;
for(int i=head[u];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(e[i].v>0 && dep[u]+1==dep[to])
{
ll tmp=dfs(to,min(flow,e[i].v));
if(tmp<=0) continue;
flow-=tmp; e[i].v-=tmp;
e[i^1].v+=tmp; res+=tmp;
if(!flow) break;
}
}
if(!res) dep[u]=-1;
return res;
}
void dinic()
{
while(bfs())
{
ans+=dfs(1,1e18);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int t,ca=0; scanf("%d",&t);
while(t--)
{
cnt=-1; ans=0;
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
int x,y,b;ll a;
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld%d",&x,&y,&a,&b);
x++; y++;
if(!b)
{
add(x,y,maxm*a+1);
add(y,x,0);
}
else
{
add(x,y,maxm*a+1);
add(y,x,maxm*a+1);
}
}
dinic();
printf("Case %d: %lld\n",++ca,ans%maxm);
}
return 0;
}
2.P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查
这是典型的二者选其一的最小割问题
我们将每个人向S,T两个超级源点建立单向边,再将所有小朋友之间建立双向边,然后求一下最小割=最大流即可
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=305;
const int maxm=45005;
int n,m,s=0,t=305;
int head[maxm],cnt=-1;
typedef long long ll;
struct edge
{
int to,nxt;
ll v;
}e[maxm<<2];
void add(int x,int y,ll z)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
e[cnt].v=z;
head[x]=cnt;
}
int dep[maxm];
bool bfs()
{
memset(dep,-1,sizeof(dep));
queue <int> q;
q.push(s);
dep[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(dep[to]==-1 && e[i].v>0)
{
q.push(to);
dep[to]=dep[u]+1;
}
}
}
return (dep[t]!=-1);
}
ll dfs(int u,ll flow)
{
if(u==t) return flow;
ll res=0;
for(int i=head[u];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(e[i].v>0 && dep[to]==dep[u]+1)
{
ll tmp=dfs(to,min(flow,e[i].v));
flow-=tmp; res+=tmp;
e[i].v-=tmp; e[i^1].v+=tmp;
if(!flow) break;
}
}
if(!flow) dep[u]=-1;
return res;
}
void dinic()
{
ll res=0;
while(bfs())
{
res+=dfs(s,1e18);
}
printf("%lld",res);
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
int x,y;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(x==1) add(s,i,1),add(i,s,0);
else add(i,t,1),add(t,i,0);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y,1); add(y,x,1);
}
dinic();
return 0;
}
3.poj2135 Farm Tour
这道题目是费用流的经典应用
求1-n的两条不重复边的路径权值和最小
我们将每条双向边,拆成两个弧,容量为1,费用为边权,再建立超级源点->1 和 n->超级汇点的容量为2,费用为0的弧
然后就是mcmf了
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n,m;
const int maxn=1005;
const int maxm=10005;
typedef long long ll;
const ll inf=1e18;
struct edge
{
int to,nxt;
ll v,w;
}e[maxm<<2];
int cnt=-1,head[maxn],s,t;
void add(int x,int y,ll z,ll zz)
{
e[++cnt].to=y;
e[cnt].nxt=head[x];
e[cnt].v=z;
e[cnt].w=zz;
head[x]=cnt;
e[++cnt].to=x;
e[cnt].nxt=head[y];
e[cnt].v=0;
e[cnt].w=-zz;
head[y]=cnt;
}
int pre[maxn];
ll dis[maxn];
bool spfa()
{
memset(dis,0x3f,sizeof(dis));
memset(pre,-1,sizeof(pre));
queue <int> q;
q.push(s);
dis[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(e[i].v>0 && dis[to]>dis[u]+e[i].w)
{
dis[to]=dis[u]+e[i].w;
pre[to]=i;
q.push(to);
}
}
}
return (pre[t]!=-1);
}
void mcmf()
{
ll ans=0,res=0;
while(spfa())
{
ll flow=inf;
for(int i=t;i!=s;i=e[pre[i]^1].to)
flow=min(flow,e[pre[i]].v);
ans+=flow;
for(int i=t;i!=s;i=e[pre[i]^1].to)
{
e[pre[i]].v-=flow;
e[pre[i]^1].v+=flow;
res+=flow*e[pre[i]].w;
}
}
printf("%lld\n",res);
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
cnt=-1; s=0,t=n+1;
memset(head,-1,sizeof(head));
add(s,1,2,0); add(n,t,2,0);
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,1,c);
add(b,a,1,c);
}
mcmf();
}
return 0;
}