先说PPT的思路
PPT的思路源于这句话:
对每条边 (u, v),连一条 (u, v) 容量为 1,费用为 1 的边。如果
流了表示删去这条边。
流过原图上的边表示删去这条边意味着什么呢?
令dif[u]=u的出度-入度
如图,灰边表示原图上的边,初始状态没有流过任何边
因为原图没有边被删,所以dif[u]=-1
这时,如果有一流量为1的流流过边a,那么此流只能从u在原图上的出边流出,即有一流量为1的流流过了边2,代表原图中边2被删,dif[u]=dif[u]-1=-2
因此,s到u流一流量为Δx的流,dif[u]要-Δx
同理,如果有一流量为1的流流过边b,那么此流只能从u在原图上的入边流入,即有一流量为1的流流过了边1或边3,代表原图中边1或边3被删,dif[u]=dif[u]+1=0
因此,u到t流一流量为Δx的流,dif[u]要+Δx
这样,我们就将dif[u]值的变化量,转化成了流过(s,u)边或(u,t)边的流量
如此,限制入度与出度的差的绝对值的最大值就变得简单了
因此考虑二分答案
设v=入度与出度的差的绝对值的最大值,二分v,然后求最少需要删去多少的边,判断是否可行即可
在建图时,
1.若dif[u]>=v:则dif[u]要减去一个数x(x>=0),因为-v<=dif[u]-x<=v,所以dif[u]-v<=x<=dif[u]+v,从s到u连一条上界为dif[u]+v,下界为dif[u]-v,费用为0的边
2.若-v<dif[u]<v:从s到u连一条上界为dif[u]+v,费用为0的边从u到t连一条上界为v-dif[u],费用为0的边
3.若dif[u]<=-v:从u到t连一条上界为v-dif[u],下界为-v-dif[u],费用为0的边
跑有源汇有上下界的最小费用最大流即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=60;
const int M=100005;
const int inf=0x7fffffff;
struct Edge{int u,v,f,w,nxt;
}edge[M<<1];
int s,t,ss,tt,S,T,head[N],cnt,inque[N],pre[N],dis[N];
queue<int> que;
int Q,n,m,K,a[M],b[M],dif[N];
void add(int u,int v,int f,int w){edge[cnt].u=u;edge[cnt].v=v;edge[cnt].w=w;edge[cnt].f=f;edge[cnt].nxt=head[u];head[u]=cnt++;edge[cnt].u=v;edge[cnt].v=u;edge[cnt].w=-w;edge[cnt].f=0;edge[cnt].nxt=head[v];head[v]=cnt++;
}
bool spfa(){memset(dis,0x7f,sizeof(dis));memset(inque,0,sizeof(inque));memset(pre,-1,sizeof(pre));dis[S]=0;que.push(S);inque[S]=1;while(!que.empty()){int u=que.front();que.pop();inque[u]=0;for(int i=head[u];i!=-1;i=edge[i].nxt){int v=edge[i].v;if(edge[i].f>0&&dis[v]>dis[u]+edge[i].w){dis[v]=dis[u]+edge[i].w;pre[v]=i;if(!inque[v]){que.push(v);inque[v]=1;}}} }if(pre[T]==-1) return 0;return 1;
}
int EK(){int flow,ret=0;while(spfa()){flow=inf;int x=pre[T];while(x!=-1){flow=min(edge[x].f,flow);x=pre[edge[x].u];}x=pre[T];while(x!=-1){edge[x].f-=flow;edge[x^1].f+=flow;ret+=flow*edge[x].w;x=pre[edge[x].u];}}return ret;
}
bool check(int v){cnt=0;memset(head,-1,sizeof(head));s=n+1;t=n+2;ss=n+3;tt=n+4;add(t,s,inf,0);for(int i=1;i<=m;i++)add(a[i],b[i],1,1);for(int i=1;i<=n;i++){if(dif[i]>=v){add(s,i,2*v,0);add(ss,i,dif[i]-v,0);add(s,tt,dif[i]-v,0);//add(s,i,[dif[i]+v,dif[i]-v],0);//减法上下界 }else if(dif[i]>-v){add(s,i,dif[i]+v,0);//减法上界 add(i,t,v-dif[i],0);//加法上界 }else{add(i,t,2*v,0);add(ss,t,-v-dif[i],0);add(i,tt,-v-dif[i],0);//add(i,t,[v-dif[i],-v-dif[i]],0);//加法上下界 }}S=ss,T=tt;if(EK()<=K) return 1;return 0;
}
int main(){scanf("%d",&Q);for(int cas=1;cas<=Q;cas++){memset(dif,0,sizeof(dif));scanf("%d%d%d",&n,&m,&K);K=m-K;for(int i=1;i<=m;i++){scanf("%d%d",&a[i],&b[i]);dif[b[i]]--;dif[a[i]]++;}int l=0,r=n,mid,ans;while(l<=r){mid=(l+r)/2;if(check(mid)){ans=mid;r=mid-1;}else l=mid+1;} printf("Case %d: %d\n",cas,ans);}return 0;
}
接下来是我自己的思考产物,还未验证
考虑如何转化 入度与出度之差。
设原图每条边流量为1,入度与出度之差转化为一个点的流入量与流出量之差。
此时图是不满足流量平衡的,所以对每个点,我们给少的流一个来处,多的流一个去处,
入度与出度之差就转化为从来处到节点的流(或从节点到去处的流)的大小。
顺着想下去,原图上的边有流流过,就代表选择了这条边,没有流流过,就代表边被删除
考虑如何求解
原题:最多k条边是限制,入度与出度之差的绝对值的最大值最小是求最值
考虑二分答案,入度与出度之差的绝对值变为限制(用限制流量实现),那么删去边数自然转为求的最值(用费用求),与d比较判断即可
update:
我的思路会产生一个问题,从源点到各节点的流的大小 代表 出度-入度, 从各节点到汇点的流的大小 代表 入度-出度,根据每个节点是出度大还是入度大,我们选择连(s,u)还是(u,t)(显然一个节点不能同时连两种边),但因为题目限制的是 出度与入度之差的绝对值,无论连哪种边,其下界都会是负数,目前我想到的唯一解决方法是将所有边的边界整体加上n