版权声明:本博客内容基本为原创,如有问题欢迎联系,转载请注明出处 https://blog.csdn.net/qq_41955236/article/details/82631008
题目链接:https://nanti.jisuanke.com/t/31447
题意:
给出一张二分图,初始每个节点的度数都为零。选择若干条边,使得每个节点的度数范围再[L,R]范围内。每选一条边,边上两端的节点度数+1。
做法:
这道题就是:建一个虚拟源点和一个虚拟汇点,虚拟源点到左边每个点连一条流量为R-L的边,右边每个点到虚拟汇点连一条流量为R-L的边。中间二分图的每条边流量当然为1,由汇点向源点连一条流量无限大的边。之后再建一个超级源点和一个超级汇点,由超级源点向左边每个点连一条流量为L的边,由右边每个点向超级汇点连一条流量为L的边。跑一遍最大流,如果超级源点和超级汇点所连的边跑满(因为一定相等),则存在可行解,否则不存在。
#include<bits/stdc++.h>
using namespace std;
const int Ni = 40005;
const int MAX = 1<<26;
struct Edge{
int u,v,c;
int next;
}edge[3*Ni];
int n,m,cnt,head[Ni],d[Ni],sp,tp,heads[Ni];//原点,汇点
int l,r;
void add(int u,int v,int c){
edge[cnt].u=u; edge[cnt].v=v; edge[cnt].c=c;
edge[cnt].next=head[u]; head[u]=cnt++;
edge[cnt].u=v; edge[cnt].v=u; edge[cnt].c=0;
edge[cnt].next=head[v]; head[v]=cnt++;
}
int bfs(){
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=edge[i].next){
int u=edge[i].v;
if(d[u]==-1 && edge[i].c>0){
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp] != -1;
}
int dfs(int a,int b){
int r=0;
if(a==tp)return b;
for(int i=heads[a];i!=-1 && r<b;i=edge[i].next)
{
int u=edge[i].v;
if(edge[i].c>0 && d[u]==d[a]+1)
{
int x=min(edge[i].c,b-r);
heads[a]=i;
x=dfs(u,x);
r+=x;
edge[i].c-=x;
edge[i^1].c+=x;
}
}
if(!r)d[a]=-2;
return r;
}
int dinic(int sp,int tp){
int total=0,t;
while(bfs()){
memcpy(heads,head,sizeof(head));
while(t=dfs(sp,MAX))
total+=t;
}
return total;
}
int main(){
int i,u,v,k,cas=0;
while(~scanf("%d%d%d",&n,&m,&k)){
scanf("%d%d",&l,&r);
cnt=0;
memset(heads,-1,sizeof(heads));
memset(head,-1,sizeof(head));
sp=n+m+1,tp=n+m+2; //附加源,附加汇
int s=n+m+3,t=n+m+4;
for(int i=1;i<=k;i++){
scanf("%d%d",&u,&v);
add(u,v+n,1);
}
for(int i=1;i<=n;i++){
add(s,i,r-l); //源点连向所有左侧点,修改流量为R-L
add(sp,i,l); //附加源连向所有左侧点,流量为L,表示必要弧
add(s,tp,l); //源点连向附加汇
}
for(int i=1;i<=m;i++){
int aim=i+n;
add(aim,t,r-l); //右侧点连向汇点,修改流量为R-L
add(aim,tp,l); //附加源连向汇点
add(sp,t,l); //右侧点连向附加汇,流量为L,表示必要弧
}
add(t,s,MAX);
printf("Case %d: ",++cas);
int ans=dinic(sp,tp); //求附加源到附加汇的最大流,若满足
if(ans==(n+m)*l) printf("Yes\n");//附加源到附加汇的所有弧都满流,则有可行流
else printf("No\n");
}
return 0;
}
再贴一个zkw的最大流,据说是适用于稠密图
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<queue>
using namespace std;
typedef long long ll;
ll N,M,K;
namespace MCMF {
ll INF=1<<29;
const ll MX=5010;
ll S, T;//源点,汇点
ll erear, n;
ll st, en, maxflow, mincost;
bool vis[MX];
ll Head[MX], cur[MX], dis[MX];
const ll ME = 1e5 + 5;//边的数量
queue <int> Q;
struct Edge
{
ll v, cap, cost, nxt, flow;
Edge() {}
Edge(ll a, ll b, ll c, ll d)
{
v = a, cap = b, cost = c, nxt = d, flow = 0;
}
} E[ME], SE[ME];
void init(ll _n)
{
n = _n, erear = 0;
for(ll i = 0; i <= n; i++) Head[i] = -1;
}
void addedge(ll u, ll v, ll cap, ll cost)
{
E[erear] = Edge(v, cap, cost, Head[u]);
Head[u] = erear++;
E[erear] = Edge(u, 0, -cost, Head[v]);
Head[v] = erear++;
}
bool adjust() {
ll v, min = INF;
for(ll i = 0; i <= n; i++)
{
if(!vis[i]) continue;
for(ll j = Head[i]; ~j; j = E[j].nxt)
{
v = E[j].v;
if(E[j].cap - E[j].flow)
{
if(!vis[v] && dis[v] - dis[i] + E[j].cost < min)
{
min = dis[v] - dis[i] + E[j].cost;
}
}
}
}
if(min == INF) return false;
for(ll i = 0; i <= n; i++)
{
if(vis[i])
{
cur[i] = Head[i];
vis[i] = false;
dis[i] += min;
}
}
return true;
}
ll augment(ll i, ll flow)
{
if(i == en)
{
mincost += dis[st] * flow;
maxflow += flow;
return flow;
}
vis[i] = true;
for(ll j = cur[i]; j != -1; j = E[j].nxt)
{
ll v = E[j].v;
if(E[j].cap == E[j].flow) continue;
if(vis[v] || dis[v] + E[j].cost != dis[i]) continue;
ll delta = augment(v, std::min(flow, E[j].cap - E[j].flow));
if(delta)
{
E[j].flow += delta;
E[j ^ 1].flow -= delta;
cur[i] = j;
return delta;
}
}
return 0;
}
void spfa()
{
ll u, v;
for(ll i = 0; i <= n; i++)
{
vis[i] = false;
dis[i] = INF;
}
Q.push(st);
dis[st] = 0; vis[st] = true;
while(!Q.empty())
{
u = Q.front(), Q.pop(); vis[u] = false;
for(ll i = Head[u]; ~i; i = E[i].nxt)
{
v = E[i].v;
if(E[i].cap == E[i].flow || dis[v] <= dis[u] + E[i].cost) continue;
dis[v] = dis[u] + E[i].cost;
if(!vis[v])
{
vis[v] = true;
Q.push(v);
}
}
}
for(ll i = 0; i <= n; i++)
{
dis[i] = dis[en] - dis[i];
}
}
ll zkw(ll s, ll t, ll &ret_flow)
{
st = s, en = t;
spfa();
mincost = maxflow = 0;
for(ll i = 0; i <= n; i++)
{
vis[i] = false;
cur[i] = Head[i];
}
do {
while(augment(st, INF))
{
memset(vis, false, n * sizeof(bool));
}
} while(adjust());
ret_flow = maxflow;
return mincost;
}
/*
void out(ll pp)
{
for(ll i=0;i<pp;i+=2)
{
if(E[i].flow==E[i].cap)printf("%d ",i/2+1);
}
printf("\n");
}
*/
}
int main()
{
ll kase=1;
while(scanf("%lld%lld%lld",&N, &M,&K)!=EOF)
{
ll source=N+M+1;
ll sink=source+1;
ll L,R;
scanf("%lld%lld",&L,&R);
MCMF::init(N+M+2);
while (K--)
{
ll u,v;
scanf("%lld%lld",&u,&v);
MCMF::addedge(u,v+N,1,0);
}
for (ll i=1;i<=N;i++)
{
MCMF::addedge(source,i,L,-1);
if (R>L)
MCMF::addedge(source,i,R-L,0);
}
for (ll i=1;i<=M;i++)
{
MCMF::addedge(i+N,sink,L,-1);
if (R>L)
MCMF::addedge(i+N,sink,R-L,0);
}
ll cost, flow;//最小费用 最大流
cost=-MCMF::zkw(source, sink, flow);
//printf("cost:%lld\n",cost);
printf("Case %d: ",kase++);
if(cost==(N+M)*L) printf("Yes\n");
else printf("No\n");
}
return 0;
}