网络流笔记
拆点
运用拆点思想,将一个点拆成入点和出点方便处理次数限制
#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;
}

浙公网安备 33010602011771号