线性规划与网络流24题 题解及部分代码以及小结

26 篇文章 0 订阅
7 篇文章 0 订阅

1.飞行员配对方案问题
最大匹配

2 太空飞行计划问题
之前写的详细题解
最大权闭合子图->最小割

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=10010;
const int inf=0x3f3f3f3f;
queue<int> Q;
int S,T,n,m,head[N],to[2*N],w[2*N],nxt[2*N],num=1,sum=0,dep[N];
bool vis[N];
void build(int u,int v,int ww)
{
    num++;
    to[num]=v; nxt[num]=head[u]; w[num]=ww; head[u]=num;
    num++;
    to[num]=u; nxt[num]=head[v]; w[num]=0; head[v]=num;
}
bool bfs()
{
    while(!Q.empty()) Q.pop();
    memset(vis,0,sizeof(vis));
    Q.push(S); vis[S]=1; dep[S]=1;
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(vis[v]||w[i]<=0) continue;
            dep[v]=dep[u]+1;
            vis[v]=1; Q.push(v);
        }
    }
    return vis[T];
}
int dfs(int u,int d)
{
    if(u==T||d==0) return d;
    int ret=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(dep[v]!=dep[u]+1||w[i]<=0) continue;
        int flow=dfs(v,min(d,w[i]));
        d-=flow; ret+=flow;
        w[i]-=flow; w[i^1]+=flow;
        if(d==0) break;
    }
    if(ret==0) dep[u]=-1;
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    S=n+m+1; T=n+m+2;
    for(int i=1;i<=n;i++)
    {
        int c; scanf("%d",&c);
        build(S,i,c); sum+=c;
        while(getchar()==' ')
        {
            int x;scanf("%d",&x);
            build(i,n+x,inf);
        }
    }
    for(int i=1;i<=m;i++)
    {
        int c;scanf("%d",&c);
        build(n+i,T,c);
    }
    while(bfs()) sum-=dfs(S,inf);
    for(int i=1;i<=n;i++) if(vis[i]) printf("%d ",i); puts("");
    for(int i=n+1;i<=n+m;i++) if(vis[i]) printf("%d ",i-n); puts("");
    printf("%d\n",sum);
    return 0;
}

3 最小路径覆盖问题
最小路径覆盖->点数-最大匹配

4 魔术球问题
最小路径覆盖

转化为判定性问题。
在n根柱子上最多能放多少个球->x个球n根柱子能不能放下->x个球最少需要几个柱子。
由于依次增加x是在上一次基础上增广,所以反而比二分快。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=10100;
const int inf=0x3f3f3f3f;
queue<int> Q;
int head[N],to[N*20],nxt[N*20],w[N*20],num=1,tail=3,S,T,ans,dep[N],pre[N];
int id[N],pos[N],n;
bool vis[N];
void build(int u,int v,int ww)
{
    num++;
    to[num]=v; nxt[num]=head[u];
    head[u]=num; w[num]=ww;
    num++;
    to[num]=v; nxt[num]=head[v];
    head[v]=num; w[num]=0;
}
bool bfs()
{
    memset(vis,0,sizeof(vis));
    Q.push(S); vis[S]=1; dep[S]=1;
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(vis[v]||w[i]<=0) continue;
            vis[v]=1; dep[v]=dep[u]+1;
            Q.push(v);
        }
    }
    return vis[T];
}
int dfs(int u,int d)
{
    if(u==T||d==0) return d; int ret=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(dep[v]!=dep[u]+1||w[i]<=0) continue;
        int flow=dfs(v,min(d,w[i]));
        ret+=flow; d-=flow;
        if(flow) pre[u]=v;
        w[i]-=flow; w[i^1]+=flow;
        if(d==0) break;
    }
    if(ret==0) dep[u]=-1;
    return ret;
}
void print(int top)
{
    memset(head,0,sizeof(head)); num=1;
    for(int i=1;i<=top;i++)
    {
        build(S,id[i],1);
        build(id[i]^1,T,1);
        for(int j=(int)sqrt(i);;j++)
        {
            int x=j*j-i;
            if(x>=i) break; if(x<=0) continue;
            build(id[x],id[i]^1,1);
        }
    }
    int ret=0;
    memset(pre,0,sizeof(pre));
    while(bfs()) ret+=dfs(S,inf);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=top;i++)
    {
        if(vis[i]) continue;
        for(int cur=i;;)
        {
            printf("%d ",cur);
            vis[cur]=1;
            int tmp=id[cur];
            tmp=pre[tmp]; cur=pos[tmp];
            if(!cur) {puts(""); break;}
        }
    }
}
int main()
{
    scanf("%d",&n);
    S=2; T=3; int ret=0;
    for(int i=1;;i++)
    {
        id[i]=++tail; pos[tail]=i; tail++; pos[tail]=i;
        build(S,id[i],1);
        build(id[i]^1,T,1);
        for(int j=(int)sqrt(i);;j++)
        {
            int x=j*j-i;
            if(x>=i) break; if(x<=0) continue;
            build(id[x],id[i]^1,1);
        }
        while(bfs()) ret+=dfs(S,inf);
        if(i-ret>n) {ans=i-1; break;}
    }
    printf("%d\n",ans);
    print(ans);
    return 0;
}

5 圆桌问题
最大流

6 最长递增子序列问题
转化为经典模型:找出最多的不相交路径数。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=10000;
queue<int> Q;
int n,head[N],to[20*N],w[20*N],nxt[20*N],num=1,S,T,s;
int dp[N],a[N],dep[N];
bool vis[N];
void build(int u,int v,int ww)
{
    num++;
    to[num]=v; nxt[num]=head[u];
    head[u]=num; w[num]=ww;
    num++;
    to[num]=u; nxt[num]=head[v];
    head[v]=num; w[num]=0;
}
bool bfs()
{
    memset(vis,0,sizeof(vis)); dep[S]=1;
    Q.push(S); vis[S]=1;
    while(!Q.empty())
    {
        int  u=Q.front(); Q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(vis[v]||w[i]<=0) continue;
            vis[v]=1; dep[v]=dep[u]+1;
            Q.push(v);
        }
    }
    return vis[T];
}
int dfs(int u,int d)
{
    if(u==T||d==0) return d; int ret=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(dep[v]!=dep[u]+1||w[i]<=0) continue;
        int flow=dfs(v,min(w[i],d));
        d-=flow; ret+=flow;
        w[i]-=flow; w[i^1]+=flow;
        if(!d) break;
    }
    if(ret==0) dep[u]=-1;
    return ret;
}
int main()
{
    scanf("%d",&n); S=2*n+1; T=2*n+2; s=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]); dp[i]=1;
        build(i,i+n,1);
        for(int j=1;j<i;j++) if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1);
        if(dp[i]==1) build(S,i,inf);
        else for(int j=1;j<i;j++) if(a[i]>a[j]&&dp[i]==dp[j]+1) build(j+n,i,1);
        s=max(s,dp[i]);
    }
    for(int i=1;i<=n;i++) if(dp[i]==s) build(i+n,T,inf);
    printf("%d\n",s);
    if(s==1) {printf("%d\n%d\n",n,n); return 0;}
    int ret=0;
    while(bfs()) ret+=dfs(S,inf);
    printf("%d\n",ret);
    build(1,1+n,inf); build(n,n+n,inf);
    while(bfs()) ret+=dfs(S,inf);
    printf("%d\n",ret);
    return 0;
}

7 试题库问题
最大流

8 机器人路径规划问题
???

9 方格取数问题
方格->染色->二分图
二分图最大点权独立集-> 总的-最小割
其实感觉和最大权闭合子图有点像?

10 餐巾计划问题
最小费用最大流
重点是找到 可以看成 入=出(xx=xx就可以看作流量平衡了) 的量,有源汇这样才能建出图。
旧代码。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=410;
const int E=100005;
queue<int> q;
int inf,M,S,T,cnt=0;
int head[N],to[E],nxt[E],w[E],c[E],flow[N],pre[N],prev[N],dis[N],num=1,nn[205],p,m,n,f,s;
bool vis[N],inq[N];
int min(int a,int b) {return a<b?a:b;}
void build(int u,int v,int ww,int cos)
{
    num++;
    to[num]=v; w[num]=ww; c[num]=cos; nxt[num]=head[u]; head[u]=num;
    num++;
    to[num]=u; w[num]=0; c[num]=-cos; nxt[num]=head[v]; head[v]=num;
}
bool spfa()
{
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    memset(inq,0,sizeof(inq));
    memset(dis,127,sizeof(dis));
    memset(flow,inf,sizeof(flow));
    memset(pre,0,sizeof(pre));
    q.push(S); dis[S]=0;  inq[S]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop(); inq[u]=0; vis[u]=1;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(w[i]<=0) continue;
            if(dis[v]>dis[u]+c[i])
            {   
                dis[v]=dis[u]+c[i];         
                flow[v]=min(flow[u],w[i]);
                pre[v]=u; prev[v]=i;
                if(!inq[v]) inq[v]=1,q.push(v);
            }
        }

    }
    return vis[T];
}
int getcos()
{
    int now=T; int ans=0; int d=flow[T];
    while(now!=S)
    {
        ans+=d*c[prev[now]];
        w[prev[now]]-=d;
        w[prev[now]^1]+=d;
        now=pre[now];
    }
    return ans;
}
int main()
{
    scanf("%d",&M);
    memset(head,0,sizeof(head)); num=1;
    memset(dis,127,sizeof(dis)); inf=dis[0];
    S=2*M+2; T=2*M+3;
    for(int i=1;i<=M;i++) scanf("%d",&nn[i]);
    scanf("%d%d%d%d%d",&p,&m,&f,&n,&s);
    for(int i=1;i<=M;i++)
    {   
        build(S,i,nn[i],0);
        if(i+m<=M) build(i,i+m+M,inf,f);
        if(i+n<=M) build(i,i+n+M,inf,s);
        if(i<M) build(i,i+1,inf,0);
        build(i+M,T,nn[i],0);
        build(S,i+M,inf,p);
    }
    int answer=0;
    while(spfa()) answer+=getcos(); 
    printf("%d",answer);    
    return 0;
}

11 航空路线问题
把起点拆了,找到两条最长的不相交路径 ->最大费用最大流

12 软件补丁问题
状压最短路

13 星际转移问题
妙妙。总之又是转化为判定性问题,按天数拆点,然后枚举天数加新点新边,看什么时候达到k。
要预先判无解的情况,并查集一下就好。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=3000;
const int M=600000;
const int inf=0x3f3f3f3f;
queue<int> Q;
int head[N],to[M],nxt[M],w[M],num=1,n,m,k,H[N],P[N],R[30][30],S,T,fa[N],dep[N];
bool vis[N];
int getfa(int x) {return (fa[x]==x)?x:getfa(fa[x]);}
void merge(int x,int y)
{
    int fx=getfa(x); int fy=getfa(y);
    if(fx!=fy) fa[fx]=fy;
}
void build(int u,int v,int ww)
{
    num++;
    to[num]=v; w[num]=ww;
    nxt[num]=head[u]; head[u]=num;
    num++;
    to[num]=u; w[num]=0; 
    nxt[num]=head[v]; head[v]=num;
}
bool bfs()
{
    memset(vis,0,sizeof(vis)); dep[S]=1;
    vis[S]=1; Q.push(S);
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(w[i]<=0||vis[v]) continue;
            vis[v]=1; dep[v]=dep[u]+1;
            Q.push(v);
        }
    }
    return vis[T];
}

int dfs(int u,int d)
{
    if(u==T||d==0) return d; int ret=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(dep[v]!=dep[u]+1||w[i]<=0) continue;
        int flow=dfs(v,min(d,w[i]));
        d-=flow; ret+=flow;
        w[i]-=flow; w[i^1]+=flow;
        if(d==0) break;
    }
    if(ret==0) dep[u]=-1;
    return ret;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k); // station,ship,people
    S=1,T=2; for(int i=1;i<=n+2;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&H[i],&P[i]);
        for(int j=0;j<P[i];j++) {scanf("%d",&R[i][j]); if(R[i][j]==0) R[i][j]=n+1; if(R[i][j]==-1) R[i][j]=n+2;}
        for(int j=1;j<P[i];j++) merge(R[i][j-1],R[i][j]);
    }
    if(getfa(n+1)!=getfa(n+2)) {printf("0\n"); return 0;}
    build(S,T+n+1,inf);// Attention!  
    int ret=0,ans=0;
    for(int D=1;D;D++)
    {
        for(int i=1;i<=n+1;i++) build(T+i+(D-1)*(n+1),T+i+D*(n+1),inf); //station from last day
        for(int i=1;i<=m;i++)
        {
            int cur=R[i][D%P[i]]; int pre=R[i][(D-1)%P[i]]; // station pre -> cur  nowpos=T+D*(n+1)+x;  prepos=T+(D-1)*(n+1)+x;
            if(cur==n+2&&pre!=n+2) build(pre+T+(D-1)*(n+1),T,H[i]);//moon
            else if(pre!=n+2) build(pre+T+(D-1)*(n+1),cur+T+D*(n+1),H[i]);
        }
        while(bfs()) ret+=dfs(S,inf);
        if(ret>=k) {ans=D;break;}
    }
    printf("%d\n",ans);
    return 0;
}

14 孤岛营救问题
分层图最短路

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=15;
const int MXN=100000;
const int inf=0x3f3f3f3f;
queue<int> Q;
int n,m,p,k,sss,S,T;
int id[1<<11][N][N],tail=0,Tail=0,dis[MXN],ID[N][N];
int head[MXN],to[4*MXN],nxt[4*MXN],w[4*MXN],num=0,pth[110][110],key[N][N],po[N];
int fx[4]={1,0,-1,0},fy[4]={0,1,0,-1};
bool ins[MXN];
void build(int u,int v,int ww)
{
    num++;
    to[num]=v; nxt[num]=head[u];
    w[num]=ww; head[u]=num;
}
void spfa()
{
    memset(dis,0x3f,sizeof(dis));
    memset(ins,0,sizeof(ins));
    dis[S]=0; ins[S]=1; Q.push(S);
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop(); ins[u]=0;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[v]>dis[u]+w[i])
            {
                dis[v]=dis[u]+w[i];
                if(!ins[v]) ins[v]=1,Q.push(v);
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);   
    for(int i=0;i<=10;i++) po[i]=1<<i;
    int top=1<<p; S=++tail; T=++tail;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) 
    {
        ID[i][j]=++Tail;
        for(int s=0;s<top;s++) id[s][i][j]=++tail;
    }
    scanf("%d",&k);
    for(int i=1;i<=k;i++)
    {
        int X1,Y1,X2,Y2,G; scanf("%d%d%d%d%d",&X1,&Y1,&X2,&Y2,&G);
        if(G>=1) pth[ID[X1][Y1]][ID[X2][Y2]]=pth[ID[X2][Y2]][ID[X1][Y1]]=G;
        else pth[ID[X1][Y1]][ID[X2][Y2]]=pth[ID[X2][Y2]][ID[X1][Y1]]=-1;
    }
    scanf("%d",&sss);
    for(int i=1;i<=sss;i++)
    {
        int x,y,q; scanf("%d%d%d",&x,&y,&q);
        key[x][y]|=po[q-1];
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    for(int s=0;s<top;s++)
    {
        for(int d=0;d<4;d++)
        {
            int x=i+fx[d]; int y=j+fy[d];
            if(x<=0||x>n||y<=0||y>m) continue;
            int ss=s|key[x][y]; int g=pth[ID[i][j]][ID[x][y]];
            if(g==-1) continue;
            if(g==0||((s|po[g-1])==s)) build(id[s][i][j],id[ss][x][y],1);
        }
        if(i==1&&j==1&&s==0) build(S,id[s][i][j],0);
        if(i==n&&j==m) build(id[s][i][j],T,0);
    }
    spfa();
    if(dis[T]>=inf) printf("-1\n");
    else printf("%d\n",dis[T]);
    return 0;
}

15 汽车加油行驶问题
分层图最短路

16 数字梯形问题
最大权不相交路径->最大费用最大流

17 运输问题
最小费用最大流

18 分配问题
最小费用最大流,最大费用最大流

19 负载平衡问题
最小费用最大流
这题可以看出总流出=总流入,根据与平均值的差是正是负知道是该S->i还是i->T。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=1500;
const int inf=0x3f3f3f3f;
queue<int> Q;
int n,a[N],aver=0,S,T,dis[N],ins[N],pre[N][2];
int head[N],to[4*N],nxt[4*N],w[4*N],c[4*N],num=1;
void build(int u,int v,int cc,int ww)
{
    num++;
    to[num]=v; nxt[num]=head[u];
    w[num]=ww; c[num]=cc; head[u]=num;
    num++;
    to[num]=u; nxt[num]=head[v];
    w[num]=0; c[num]=-cc; head[v]=num;
}
bool spfa()
{
    memset(dis,0x3f,sizeof(dis));
    memset(ins,0,sizeof(ins));
    dis[S]=0; Q.push(S); ins[S]=1;
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop(); ins[u]=0;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i]; if(w[i]<=0) continue;
            if(dis[v]>dis[u]+c[i])
            {
                dis[v]=dis[u]+c[i];
                pre[v][0]=u; pre[v][1]=i;
                if(!ins[v]) ins[v]=1,Q.push(v);
            }
        }
    }
    return dis[T]<inf;
}
int get()
{
    int flow=inf; int ret=0; int tmp=T;
    while(tmp!=S)
    {
        flow=min(flow,w[pre[tmp][1]]);
        tmp=pre[tmp][0];
    }
    tmp=T;
    while(tmp!=S)
    {
        ret+=c[pre[tmp][1]]*flow;
        w[pre[tmp][1]]-=flow;
        w[pre[tmp][1]^1]+=flow;
        tmp=pre[tmp][0];
    }
    return ret;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),aver+=a[i];
    aver/=n; S=n+1; T=n+2;
    for(int i=1;i<=n;i++)
    {
        int ww=a[i]-aver;
        if(ww>0) build(S,i,0,ww);
        else build(i,T,0,-ww);
        int Pre=(i==1)?n:i-1;
        int Nxt=(i==n)?1:i+1;
        build(i,Nxt,1,inf);
        build(i,Pre,1,inf);
    }
    int ret=0; while(spfa()) ret+=get();
    printf("%d\n",ret);
    return 0;
}

20 深海机器人问题
最小费用最大流

21 最长k可重区间集问题
最大权不相交路径->最大费用最大流

最朴素的建图自然是S向每个区间连边(费用区间长度),每个区间向它所覆盖的点连边,每个点向T连边。然后限制每个点的流出量。 边数是n^2的。

想要减少边的数量,问题在于区间的建边,显然不能和区间的长度相关,最好每个区间只有 O(1)条边。

这道题妙的地方在于转化成了:求K条权之和最大的不相交路径,每条路径为一些不相交的区间序列。 这样建图也就容易想了,边数是O(n)的。

关于这个转化,我的思考过程是这样的:
首先,要保证每个点经过次数,其实只要看端点就好了,那么要离散化。
然后,我们想把“每个区间向它所覆盖的点连边”和“限制每个点的流出量”拍扁在端点的序列上。
这个序列上,前一个点向后一个点连边, 因为可以不走区间那么就是连费用0的边,而要保证走区间就是区间首向尾连容量1费用长度的边。
于是就变成了上面的那张图。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
map<int,int> mp;
const int N=100005;
const int inf=0x3f3f3f3f;
queue<int> Q;
int head[N],to[4*N],nxt[4*N],w[4*N],c[4*N],n,k,L[N],R[N],S,T,tail=2,num=1;
int dis[N],pre[N][2],val[2*N],tot=0;
bool ins[N];
void build(int u,int v,int cc,int ww)
{
    num++;
    to[num]=v; nxt[num]=head[u];
    head[u]=num; w[num]=ww; c[num]=cc;
    num++;
    to[num]=u; nxt[num]=head[v];
    head[v]=num; w[num]=0; c[num]=-cc;
}

bool spfa()
{
    memset(ins,0,sizeof(ins));
    memset(dis,-1,sizeof(dis));
    dis[S]=0; ins[S]=1; Q.push(S);
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop(); ins[u]=0;
        for(int i=head[u];i;i=nxt[i])
        {
            if(w[i]<=0) continue; int v=to[i];
            if(dis[v]<dis[u]+c[i])
            {
                dis[v]=dis[u]+c[i];
                pre[v][0]=u; pre[v][1]=i;
                if(!ins[v]) ins[v]=1,Q.push(v);
            }
        }
    }
    return dis[T]!=-1;
}
int get()
{
    int flow=inf; int ret=0; int tmp=T;
    while(tmp!=S)
    {
        flow=min(flow,w[pre[tmp][1]]);
        tmp=pre[tmp][0];
    }
    tmp=T;
    while(tmp!=S)
    {
        ret+=flow*c[pre[tmp][1]];
        w[pre[tmp][1]]-=flow;
        w[pre[tmp][1]^1]+=flow;
        tmp=pre[tmp][0];
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d",&L[i],&R[i]),val[++tot]=L[i],val[++tot]=R[i];
    S=1,T=2,tail=2;
    sort(val+1,val+tot+1);
    for(int i=1,last=0;i<=tot;i++) if(last!=val[i]) 
    {
        mp[val[i]]=++tail;
        if(last==0) build(S,tail,0,k);
        else build(tail-1,tail,0,k);
        last=val[i];
    }
    build(tail,T,0,k);
    for(int i=1;i<=n;i++) build(mp[L[i]],mp[R[i]],R[i]-L[i],1);
    int ret=0;
    while(spfa()) ret+=get();
    printf("%d\n",ret);
    return 0;
}

22 最长k可重线段集问题
最大权不相交路径->最大费用最大流

23 火星探险问题
最小费用最大流

24 骑士共存问题
方格染色二分图->最大独立集->最小割


总结:

对于网络流的模型转化,很多时候都需要找到 xx=xx的等式,
例如像bzoj1061的不等式->等式,要求一个变量出现两次,
或者像是负载平衡问题 ,餐巾问题。
这都是由于网络流流入量=流出量的性质所延伸的问题。

很多求最大最小的问题,可转化成经典的判定性问题+二分/枚举。

同时,像是 方格->二分图等的经典模型也提醒我们观察是否有可以构成二分图的东西。

另外,这24题中的很多问题都可以用动归求解,方法不止一种。


除了以上的那些经典建图,网络流的“后悔机制”往往也生出一些比较精妙的模型,
例如之前多校联训Day6的T3,以及bzoj2504危桥,bzoj4669抢夺。

以及一些看似有后效性的东西,为了预先计算对后面的影响,设定一些特殊的费用以转化模型,
例如bzoj1070修车,bzoj2597剪刀石头布。

很多题目的模型都不是轻松能转化过来的,要细心观察。


最后附上 ByVoid神的题解集合:https://www.byvoid.com/zhs/blog/lpf24-solution?amp=1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值