网络流笔记

拆点

P2045 方格取数加强版

运用拆点思想,将一个点拆成入点和出点方便处理次数限制

#define int long long
using namespace std;
int x[55][55],dis[6000],vis[6000],n,k;
queue<int>q;
int head[6000],fir[6000],nxt[30000],val[30000],to[30000],w[30000],cnt=1;
void add(int u,int v,int W,int V){
	nxt[++cnt]=head[u];
	head[u]=cnt;
	val[cnt]=V;
	to[cnt]=v;
	w[cnt]=W;
	return;
}
bool SPFA(){
	for(int i=0;i<=n*n*2;i++){
		fir[i]=head[i];
		dis[i]=-1;
	}
	while(!q.empty())q.pop();
	dis[0]=0;
	q.push(0);
	while(!q.empty()){
		int now=q.front();
		q.pop();
		for(int i=head[now];i;i=nxt[i])if(w[i]>0&&dis[to[i]]<dis[now]+val[i]){
			dis[to[i]]=dis[now]+val[i];
			q.push(to[i]);
		}
	}
	return dis[n*n*2]>0;
}
int dfs(int u,int minn){
	if(!minn)return 0;
	if(u==n*n*2)return minn;
	int now=0;
	vis[u]=1;
	for(int i=fir[u];i;i=nxt[i]){
		fir[u]=i;
		if(w[i]>0&&minn&&!vis[to[i]]&&dis[u]+val[i]==dis[to[i]]){
			int go=dfs(to[i],min(minn,w[i]));
			now+=go;
			w[i]-=go;
			w[i^1]+=go;
			minn-=go;
		}
	}
	vis[u]=0;
	return now;
}
signed main(){
	scanf("%lld%lld",&n,&k);
	for(int i=0;i<n;i++)for(int j=1;j<=n;j++)scanf("%lld",&x[i][j]);
	for(int i=0;i<n;i++){
		for(int j=1;j<=n;j++){
			add(i*n+j,n*n+i*n+j,1,x[i][j]);
			add(n*n+i*n+j,i*n+j,0,-x[i][j]);
			add(i*n+j,n*n+i*n+j,100,0);
			add(n*n+i*n+j,i*n+j,0,0);
			if(j<n){
				add(n*n+i*n+j,i*n+j+1,100,0);
				add(i*n+j+1,n*n+i*n+j,0,0);
			}
			if(i<n-1){
				add(n*n+i*n+j,i*n+j+n,100,0);
				add(i*n+j+n,n*n+i*n+j,0,0);
			}
		}
	}
	add(0,1,k,0);
	int now=0,ans=0;
	while(SPFA())while(now=dfs(0,1e10))ans+=now*dis[n*n*2];
	printf("%lld\n",ans);
	return 0;
}

通过边的流量限制总数量

通过限制总流量小于等于 \(k\),从而限制同时最多选中 \(k\) 个区间,从而将问题转化为网络流。

#include<bits/stdc++.h>
#define int long long
#define N 1005
#define M 5005
using namespace std;
int L[N],R[N],vis[N],dis[N],num[N],m,n,k;
int cnt=1,head[N],fir[N],to[M],nxt[M],w[M],v[M];
queue<int>q;
bool topo(){
    for(int i=0;i<=m+1;i++){
        dis[i]=-1;
        fir[i]=head[i];
    }
    while(!q.empty())q.pop();
    dis[0]=0;
    q.push(0);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=nxt[i])if(w[i]>0&&dis[to[i]]<dis[u]+v[i]){
            q.push(to[i]);
            dis[to[i]]=dis[u]+v[i];
        }
    }
    return dis[m+1]>0;
}
int solve(int u,int minn){
    int go=0;
    if(u==m+1)return minn;
    vis[u]=1;
    for(int i=fir[u];i;i=nxt[i])if(minn>0&&w[i]>0&&!vis[to[i]]&&dis[to[i]]==dis[u]+v[i]){
        fir[u]=i;
        int now=solve(to[i],min(w[i],minn));
        minn-=now;
        w[i]-=now;
        w[i^1]+=now;
        go+=now;
    }
    vis[u]=0;
    return go;
}
void add(int u,int V,int val,int W){
    nxt[++cnt]=head[u];
    head[u]=cnt;
    w[cnt]=W;
    to[cnt]=V;
    v[cnt]=val;
    return;
}
signed main(){
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&L[i],&R[i]);
        num[i*2-1]=L[i];
        num[i*2]=R[i];
    }
    sort(num+1,num+n*2+1);
    m=unique(num+1,num+n*2+1)-num-1;
    for(int i=1;i<=n;i++){
        L[i]=lower_bound(num+1,num+m+1,L[i])-num;
        R[i]=lower_bound(num+1,num+m+1,R[i])-num;
    }
    for(int i=0;i<=m;i++)add(i,i+1,0,k),add(i+1,i,0,0);
    for(int i=1;i<=n;i++)add(L[i],R[i],num[R[i]]-num[L[i]],1),add(R[i],L[i],num[L[i]]-num[R[i]],0);
    int ans=0,now=0;
    while(topo())while(now=solve(0,50))ans+=now*dis[m+1];
    printf("%lld\n",ans);
    return 0;
}

转化后使用网络流求解

Increase to make it Increasing

容易将题目转化为对于每种操作等价于在差分序列上,从 \(r+1\) 移动 \(1\)\(l\) 上,所有的这些玩意构成了一个 DAG。

但是拓扑做不了这个玩意,所以需要网络流。

对于每种操作,我们从 \(r+1\)\(l\) 连流量 \(inf\),边权为 \(1\) 的边。

然后对于每个点,如果其差分值大于 \(0\),那么就从 \(n+1\)\(i\) 连流量为差分值,边权为 \(0\) 的边。
如果其差分值大于 \(0\),那么就从 \(i\)\(0\) 连流量为差分值,边权为 \(0\) 的边。

然后跑 \(n+1\) 为源点,\(0\) 为汇点的最小流,通过总流量来判定无解情况。

#include<bits/stdc++.h>
#define N 305
#define inf 100000000
using namespace std;
int x[N],y[N],head[N],fir[N],dis[N],vis[N],check[N],cnt=1,n,m;
queue<int>Q;
struct Ty{int nxt,v,val,w;}w[N*4];
void add(int u,int v,int op,int val){
	w[++cnt].nxt=head[u];
	w[cnt].v=v;
	w[cnt].val=op;
	w[cnt].w=val;
	head[u]=cnt;
	w[++cnt].nxt=head[v];
	w[cnt].v=u;
	w[cnt].val=-op;
	w[cnt].w=0;
	head[v]=cnt;
	return;
}
int dinic(int u,int minn){
	if(minn==0)return 0;
	if(u==0)return minn;
	vis[u]=1;
	int got=0;
	for(int i=fir[u];i;i=w[i].nxt)if(dis[u]+w[i].val==dis[w[i].v]&&w[i].w>0&&!vis[w[i].v]&&minn>0){
		int now=dinic(w[i].v,min(minn,w[i].w));
		w[i].w-=now;
		w[i^1].w+=now;
		minn-=now;
		got+=now;
	}
	vis[u]=0;
	return got;
}
bool SPFA(){
	for(int i=0;i<=n+1;i++){
		fir[i]=head[i];
		dis[i]=1e8;
		check[i]=0;
		vis[i]=0;
	}
	dis[n+1]=0;
	Q.push(n+1);
	check[n+1]=1;
	while(!Q.empty()){
		int u=Q.front();
		Q.pop();
		check[u]=0;
		for(int i=head[u];i;i=w[i].nxt)if(w[i].w>0&&dis[u]+w[i].val<dis[w[i].v]){
			dis[w[i].v]=dis[u]+w[i].val;
			if(!check[w[i].v]){
				Q.push(w[i].v);
				check[w[i].v]=1;
			}
		}
	}
	return dis[0]<1e8;
}
signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&x[i]);
	for(int i=1;i<=n;i++)y[i]=x[i]-x[i-1];
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(v+1,u,1,inf);
	}
	int flag=0;
	for(int i=1;i<=n;i++){
		if(y[i]>0)add(n+1,i,0,y[i]);
		if(y[i]<0){
			flag+=-y[i];
			add(i,0,0,-y[i]);
		}
	}
	int summ=0,ans=0,res=0;
	while(SPFA()){
		while(res=dinic(n+1,1e8)){
			ans+=res*dis[0];
			summ+=res;
		}
	}
	if(summ!=flag){
		printf("-1\n");
		return 0;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2025-08-18 20:52  Igunareo  阅读(18)  评论(0)    收藏  举报