码迷,mamicode.com
首页 > 其他好文 > 详细

【多题合集】网络流24题练习(更新至魔术球问题)

时间:2016-04-29 19:28:16      阅读:247      评论:0      收藏:0      [点我收藏+]

标签:

飞行员配对问题(仅求方案总数)
传送门
思路:二分图后dinic走起,各边容量为1
代码:

#include<bits/stdc++.h>
#define inf 0x7ff
using namespace std;
int ans,n,n1,s,t,tot=1;
int first[105],dis[105],up[105];
bool vis[105];
queue<int>q;
struct edge
{
    int u,v,next,w;
}e[500];
void add(int x,int y,int z)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].next=first[x];
    first[x]=tot;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    memset(up,0,sizeof(up));
    dis[s]=1;
    q.push(s);
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        for (int i=first[k];i;i=e[i].next)
            if (!dis[e[i].v]&&e[i].w)
                q.push(e[i].v),
                dis[e[i].v]=dis[k]+1;
    }
    return dis[t];
}
void flow()
{
    int minn=0x7ff;
    for (int i=up[t];i;i=up[e[i].u])
    minn=min(minn,e[i].w);
    ans+=minn;
    for (int i=up[t];i;i=up[e[i].u])
    e[i].w-=minn,e[i^1].w+=minn;
}
void dfs(int x,int maxn)
{
    if (x==t) {flow();return;}
    for (int i=first[x];i;i=e[i].next)
    if (e[i].w&&dis[e[i].v]==dis[x]+1)
    up[e[i].v]=i,dfs(e[i].v,min(maxn,e[i].w));
}
main()
{
    scanf("%d%d",&n,&n1);
    int x,y;
    s=n+1;t=n+2;
        while (scanf("%d%d",&x,&y)!=EOF)
        add(x,y,1),
        add(y,x,0),
        vis[x]=1;
    for (int i=1;i<=n;i++)
    if (vis[i]) add(s,i,1),add(i,s,0);
    else add(i,t,1),add(t,i,0);
    while (bfs())
    dfs(s,0x7ff);
    printf("%d",ans);
}

太空飞行计划
传送门
思路:(在codevs上调了好久,最终弃疗)
cogs上是不用输出收益为0方案,所以我们直接学习了最大权闭合子图的姿势,求出最大流,并用所有点的正权值减去它就可以了
代码:

#include<bits/stdc++.h>
#define inf 0x7fffff
using namespace std;
int ans,n,m,tot=1,s,t;
int first[205],dis[205],up[205];
bool mac[102],exps[102];
queue<int>q;
struct edge
{
    int u,v,w,next;
}e[40000];
void add(int x,int y,int z)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].next=first[x];
    first[x]=tot;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    memset(up,0,sizeof(up));
    q.push(s);
    dis[s]=1;
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        for (int i=first[k];i;i=e[i].next)
        if (e[i].w&&!dis[e[i].v])
            dis[e[i].v]=dis[k]+1,q.push(e[i].v);
    }
    return dis[t];
}
void flow()
{
    int minn=inf;
    for (int i=up[t];i;i=up[e[i].u])
        minn=min(minn,e[i].w);
    ans-=minn;
    for (int i=up[t];i;i=up[e[i].u])
        e[i].w-=minn,
        e[i^1].w+=minn;
}
void dfs(int x,int maxn)
{
    if (x==t) {flow();return;}
    for (int i=first[x];i;i=e[i].next)
    if (e[i].w&&dis[e[i].v]==dis[x]+1)
        up[e[i].v]=i,dfs(e[i].v,min(maxn,e[i].w));
}
main()
{
    scanf("%d%d",&m,&n);
    s=m+n+1;t=m+n+2;
    int x;
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        ans+=x;
        add(s,i+n,x);
        add(i+n,s,0);
        while (1)
        {
            scanf("%d",&x);
            add(i+n,x,inf);
            add(x,i+n,0);
            if (getchar()!=‘ ‘) break;
        }
    }
    for (int i=1;i<=n;i++)
        scanf("%d",&x),add(i,t,x),add(t,i,0);
    while (bfs()) dfs(s,inf);
    for (int i=first[s];i;i=e[i].next)
        if (dis[e[i].v]) exps[e[i].v-n]=1;
    for (int i=first[t];i;i=e[i].next)
        if (dis[e[i].v]) mac[e[i].v]=1;
    for (int i=1;i<=m;i++)
        if (exps[i]) printf("%d ",i);
    puts("");
    for (int i=1;i<=n;i++)
        if (mac[i]) printf("%d ",i);
    puts("");
    printf("%d",ans);
}

圆桌问题
传送门
思路:这个构图太简单……不说了
注意:
1.时间上的优化!我之前写的这些都是扯淡!
2.别上codevs评测,没有SPJ
代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,s,t,tot=1,ans,sum;
int first[500],dis[500],num[500][500],up[500],cur[500],cnt[500];
queue<int>q;
struct edge
{
    int u,v,next,w;
}e[100000];
void add(int x,int y,int z)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].next=first[x];
    first[x]=tot;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    dis[s]=1;
    q.push(s);
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        for (int i=first[k];i;i=e[i].next)
            if (!dis[e[i].v]&&e[i].w) dis[e[i].v]=dis[k]+1,q.push(e[i].v);
    }
    if (dis[t])
        for (int i=1;i<=t;i++) cur[i]=first[i];
    return dis[t];
}
void flow()
{
    int minn=0x7fffff;
    for (int i=up[t];i;i=up[e[i].u]) minn=min(minn,e[i].w);
    if (!minn) return;
    ans+=minn;
    for (int i=up[t];i;i=up[e[i].u])
    {
        e[i].w-=minn;e[i^1].w+=minn;
        if (e[i].u<=m&&e[i].v<=n+m)
            num[e[i].u][e[i].v-m]^=1;
        else if (e[i].v<=m&&e[i].u<=n+m)
            num[e[i].v][e[i].u-m]^=1;
    }
}
int dfs(int x,int maxn)
{
    if (x==t) {flow();return maxn;}
    int used=0;
    for (int i=cur[x];i;i=e[i].next)
    if (dis[e[i].v]==dis[x]+1)
    {
        up[e[i].v]=i; 
        int flow=dfs(e[i].v,min(maxn,e[i].w));
        used+=flow;
        if (e[i].w) cur[x]=i;
        if (used==maxn) return maxn;
    }
    return used; 
}
main()
{
    scanf("%d%d",&m,&n);
    int x;
    s=n+m+1;t=n+m+2;
    for (int i=1;i<=m;i++)
        scanf("%d",cnt+i),
        sum+=cnt[i],
        add(s,i,cnt[i]),
        add(i,s,0);
    for (int i=1;i<=n;i++)
        scanf("%d",&x),
        add(i+m,t,x),
        add(t,i+m,0);
    for (int i=1;i<=m;i++)
        for (int j=1;j<=n;j++)
            add(i,j+m,1),
            add(j+m,i,0);
    while (bfs()) dfs(s,0x7ffff);
    if (ans<sum) {printf("0");return 0;}
    puts("1");
    for (int i=1;i<=m;i++)
    {
        for (int j=1;j<=n;j++)
        if (num[i][j]) printf("%d ",j);
        puts("");
    }
}

最长上升子序列问题
传送门
思路:发现网上大部分都是最大流拆点,而我写了个半残的费用流……
1.s->i连容量为1,费用为-1的边
2.i->t连容量为1,费用为0的边
3.i->j(a[i]<a[j])连容量为1,费用为-1的边
跑最小费用即可,算长度的话只用记录每次增广路上的费用总和就行了(不要乘流量,单算费用)
不用DP干掉1,2问
第3问把s->1,s->n,1->t,n->t的边容量无穷大就行了
注意:

求的是最长不下降子序列!不是最长上升子序列!这里坑爆了!

#include<bits/stdc++.h>
using namespace std;
int n,a[510],s,t,tot=1,len,ans=1;
int first[510],up[510],dis[510];
bool vis[510];
queue<int> q;
struct edge
{
    int u,v,w,next,cost;
}e[200000];
void add(int x,int y,int z,int c)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].cost=c;
    e[tot].next=first[x];
    first[x]=tot;
}
bool spfa()
{
    memset(dis,63,sizeof(dis));
    memset(up,0,sizeof(up));
    dis[s]=0;vis[s]=1;
    q.push(s);
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        vis[k]=0;
        for (int i=first[k];i;i=e[i].next)
        if (e[i].w&&dis[e[i].v]>dis[k]+e[i].cost)
        {
            dis[e[i].v]=dis[k]+e[i].cost;
            up[e[i].v]=i;
            if (!vis[e[i].v]) vis[e[i].v]=1,q.push(e[i].v);
        }
    }
    return dis[t]<0x7ffff;
}
int flow()
{
    int p=0,minn=0x7fffff;
    for (int i=up[t];i;i=up[e[i].u])
        minn=min(e[i].w,minn),
        p+=e[i].cost;
    for (int i=up[t];i;i=up[e[i].u])
        e[i].w-=minn,
        e[i^1].w+=minn;
    return p;
}
main()
{
    scanf("%d",&n);
    s=n+1;t=n+2;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
        add(s,i,1,-1);
        add(i,s,0,1);
        add(i,t,1,0);
        add(t,i,0,0);
        for (int j=1;j<i;j++)
            if (a[j]<=a[i])
                add(j,i,1,-1),
                add(i,j,0,1);
    }
    for (int i=tot+1;i<=tot*2;i++) e[i]=e[i-tot];
    while (spfa())
    {
        if (!len) len=flow();
        else if (len!=flow()) break;
        else ans++;
    }
    printf("%d\n%d\n",-len,ans);
    for (int i=1;i<=tot;i++)
    {
        e[i]=e[i+tot];
        if ((e[i].u==s&&e[i].v==n)||(e[i].u==s&&e[i].v==1)) e[i].w=0x7ff;
        if ((e[i].u==n&&e[i].v==t)||(e[i].u==1&&e[i].v==t)) e[i].w=0x7ff;
    }
    ans=1;len=0;
    while (spfa())
    {
        if (!len) len=flow();
        else if (len!=flow()) break;
        else ans++;
    }
    printf("%d",ans);
}

最小路径覆盖问题
传送门
思路:
二分图最大流,答案=点数-流量和
方案数从1-n枚举dfs找路径即可,要求流量为0
注意:输出格式注意!最后要换行!codevs上只用输出方案数!

#include<bits/stdc++.h>
using namespace std;
int n,m,tot=1,s,t;
int first[320],dis[320],cur[320],ans;
int num[320];
bool vis[320];
queue<int>q;
struct edge
{
    int u,v,w,next;
}e[15000];
void add(int x,int y,int z)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].next=first[x];
    first[x]=tot;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    dis[s]=1;
    q.push(s);
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        for (int i=first[k];i;i=e[i].next)
        if (e[i].w&&!dis[e[i].v])
            dis[e[i].v]=dis[k]+1,
            q.push(e[i].v);
    }
    if (dis[t])
        for (int i=1;i<=t;i++) cur[i]=first[i];
    return dis[t];
}
int dfs(int x,int maxn)
{
    if (x==t) {return maxn;}
    int used=0;
    for (int i=cur[x];i;i=e[i].next)
    if (dis[e[i].v]==dis[x]+1)
    {
        int k=dfs(e[i].v,min(maxn,e[i].w));
        e[i].w-=k;e[i^1].w+=k;
        used+=k;
        if (e[i].w) cur[x]=i;
        if (used==maxn) return maxn;
    }
    if (!used) dis[x]=0;
    return used;
}
void find(int x)
{
    num[++num[0]]=x;
    vis[x]=1;
    for (int i=first[x];i;i=e[i].next)
    if (!e[i].w&&e[i].v!=s&&!vis[e[i].v-n]) find(e[i].v-n);
}
main()
{
    freopen("path3.in","r",stdin);
    freopen("path3.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    for (int i=1;i<=m;i++)
        scanf("%d%d",&x,&y),
        add(x,y+n,1),
        add(y+n,x,0);
    s=(n<<1)+1;t=n+1<<1;
    for (int i=1;i<=n;i++)
        add(s,i,1),
        add(i,s,0),
        add(i+n,t,1),
        add(t,i+n,0);
    while (bfs())
    ans+=dfs(s,0x7ff);
    for (int i=1;i<=n;i++)
        if (!vis[i])
        {
            num[0]=0,find(i);
            for (int i=1;i<=num[0];i++)
            printf("%d%c",num[i]," \n"[i==num[0]]);
        }
    printf("%d\n",n-ans);
}

餐巾计划问题
传送门1
传送门2
思路:
感觉好厉害,一开始没有考虑流量守恒,建图完全错了,该连源点的连汇点,该连汇点的连了源点(s连新餐巾,t连旧餐巾)然后就一直买啊买也不洗……改一下就连买都不买了……后来看了高大哥的课件建图,感觉自己还是功力不深啊!!
技术分享
代码:

#include<bits/stdc++.h>
#define inf 0x7ffff
using namespace std;
int n,f,c1,c2,t1,t2,s,t,tot=1,ans;
bool vis[2010];
int dis[2010],first[2010],up[2010];
queue<int>q;
struct edge
{
    int u,v,w,cost,next;
}e[200000];
void add(int x,int y,int z,int c)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].cost=c;
    e[tot].next=first[x];
    first[x]=tot;
}
bool spfa()
{
    memset(dis,63,sizeof(dis));
    memset(up,0,sizeof(up));
    q.push(s);
    dis[s]=0;vis[s]=1;
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        vis[k]=0;
        for (int i=first[k];i;i=e[i].next)
            if (e[i].w&&dis[e[i].v]>dis[k]+e[i].cost)
            {
                dis[e[i].v]=dis[k]+e[i].cost;
                up[e[i].v]=i;
                if (!vis[e[i].v]) vis[e[i].v]=1,q.push(e[i].v);
            }
    }
    return dis[t]<inf;
}
void flow()
{
    int minn=inf;
    for (int i=up[t];i;i=up[e[i].u]) minn=min(e[i].w,minn);
    for (int i=up[t];i;i=up[e[i].u])
        e[i].w-=minn,
        e[i^1].w+=minn,
        ans+=minn*e[i].cost;
}
main()
{
//  scanf("%d%d%d%d%d%d",&n,&f,&t1,&c1,&t2,&c2);codevs上的

//  scanf("%d%d%d%d%d%d",&n,&t1,&t2,&f,&c1,&c2);
//  t1++;t2++;BZOJ上的
    s=(n<<1)+1;t=1+n<<1;
    int x;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i+n,t,x,0);
        add(t,i+n,0,0);
        add(s,i,x,0);
        add(i,s,0,0);
        add(s,i+n,inf,f);
        add(i+n,s,0,-f);
        if (i+1<=n) add(i,i+1,inf,0),add(i+1,i,0,0);
        if (i+t1<=n) add(i,i+t1+n,inf,c1),add(i+t1+n,i,0,-c1);
        if (i+t2<=n) add(i,i+t2+n,inf,c2),add(i+t2+n,i,0,-c2);
    }
    while (spfa()) flow();
    printf("%d",ans);
}

星际转移问题(家园)
传送门1
传送门2
思路:数据比较小,可以枚举时间动态开点连边,把图分层,各个太空站在每一个时刻都是一个独立的点,样例中的
2 2 1
1 3 0 1 2
1 3 1 2 –1
构图为这样(没有连边的原因是画出来就很乱,所以不连了)
技术分享
如果我们把时刻为t时的i点标号为st[t][i]且在这个飞船的下一站为next[i],我们要连的就是(st[t][i],st[t+1][next[i]])
同时,每个太空站是可以留人的,所以还要连(st[t][i],st[t+1][i]
然后枚举时间由1->+∞跑最大流,当总流量>=t时就可以停下了
注意:
1.我们不可能一直把时间枚举下去,差不多t=500(可能更小)的时候就可以停下输出无解了
2.给点标号着实让我蛋疼了好久,大家自己感受下吧

#include<bits/stdc++.h>
#define inf 0x7fff
using namespace std;
int ans,times,n,m,k,tot=1,s,t;
int cur[5000],dis[5000],first[5000],p[30],st[30][17],num[30];
queue<int>q;
struct edge
{
    int u,v,w,next;
}e[20000];
void add(int x,int y,int z)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].w=z;
    e[tot].next=first[x];
    first[x]=tot;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    q.push(s);dis[s]=1;
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        for (int i=first[k];i;i=e[i].next)
        if (!dis[e[i].v]&&e[i].w)
            dis[e[i].v]=dis[k]+1,q.push(e[i].v);
    }
    cur[s]=first[s];cur[t]=first[t];
    if (dis[t]) for (int i=1;i<=times*m;i++) cur[i]=first[i];
    return dis[t];
}
int dfs(int x,int maxn)
{
    if (x==t) return maxn;
    int used=0;
    for (int i=cur[x];i;i=e[i].next)
    if (dis[e[i].v]==dis[x]+1)
    {
        int k=dfs(e[i].v,min(maxn,e[i].w));
        used+=k;
        e[i].w-=k;
        e[i^1].w+=k;
        if(e[i].w) cur[x]=i;
        if (used==maxn) return maxn;
    }if (!used) dis[x]=0;
    return used;
}
main()
{
    scanf("%d%d%d",&n,&m,&k);
    s=4999;t=4998;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",p+i,num+i);
        for (int j=0;j<num[i];j++)
        {
            scanf("%d",st[i]+j);
            if (st[i][j]<=0) st[i][j]+=4999;
        }
    }
    while (++times<=500)
    {
        for (int i=1;i<=m;i++)
        {
            int x,y;
            if (st[i][(times-1)%num[i]]==s) x=s;
            else if (st[i][(times-1)%num[i]]==t) x=t;
            else x=st[i][(times-1)%num[i]]+n*(times-1);
            if (st[i][times%num[i]]==s) y=s;
            else if (st[i][times%num[i]]==t) y=t;
            else y=st[i][times%num[i]]+n*times;
            add(x,y,p[i]);
            add(y,x,0);
        }
        for (int i=n*(times-1)+1;i<=n*(times-1)+n;i++)
            add(i,i+n,inf),add(i+n,i,0);
        while (bfs()) ans+=dfs(s,0x7ff);
        if (ans>=k) printf("%d",times),exit(0);
    }
    printf("0");
}

骑士共存问题
传送门
思路:根据所在行列之和x+y的奇偶来进行二分图,然后跑dinic
s->奇 容量为1
偶数->t 容量为1
奇数->被他影响的8个点 容量为inf
最后得出的最大流就是放完最多数后的不能放的点(不包括m个点),答案就是n*n-m-maxflow
代码:

#include<bits/stdc++.h>
#define inf 0x7ffff
#define pd(x,y,z) (x<=y&&y<=z)
using namespace std;
int ans,s,t,n,m,x,y,tot=1;
int dis[80010],first[80010],cur[80010];
int dx[9]={0,-1,-2,-2,-1,1,2,2,1},dy[9]={0,-2,-1,1,2,-2,-1,1,2};
bool vis[80010];
queue<int>q;
struct edge
{
    int u,v,w,next;
}e[410000];
void add(int x,int y,int z)
{
    e[++tot]=(edge){x,y,z,first[x]};
    first[x]=tot;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    q.push(s);dis[s]=1;
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        for (int i=first[k];i;i=e[i].next)
            if (e[i].w&&!dis[e[i].v])
                q.push(e[i].v),dis[e[i].v]=dis[k]+1;
    }
    if (dis[t])
        for (int i=1;i<=t;i++) cur[i]=first[i];
    return dis[t];
}
int dfs(int x,int maxn)
{
    if (x==t) return maxn;
    int used=0;
    for (int i=cur[x];i;i=e[i].next)
    if (dis[e[i].v]==dis[x]+1)
    {
        int k=dfs(e[i].v,min(maxn,e[i].w));
        e[i].w-=k;
        e[i^1].w+=k;
        if (e[i].w) cur[x]=i;
        used+=k;
        if (used==maxn) return maxn;
    }
    if (!used) dis[x]=0;
    return used;
}
main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
        scanf("%d%d",&x,&y),
        vis[(x-1)*n+y]=1;
    s=n*n+1;t=n*n+2;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        if (!vis[(i-1)*n+j])
        {
            x=(i-1)*n+j;
            if ((i+j)&1)
            {
                add(s,x,1);add(x,s,0);
                for (int k=1;k<=8;k++)
                if (pd(1,i+dx[k],n)&&pd(1,j+dy[k],n))
                    add(x,n*(i+dx[k]-1)+j+dy[k],inf),
                    add(n*(i+dx[k]-1)+j+dy[k],x,0);
            }
            else add(x,t,1),add(t,x,0);
        }
    while (bfs())
        ans+=dfs(s,inf);
    printf("%d",n*n-ans-m);
}

魔术球问题(简化版)
传送门
思路:枚举答案,动态加点判断,但一开始我是暴力加边,多建了一层关于“柱子”的点,只过了9个点,后来看到黄学长的blog才幡然醒悟,合并点,这里直接求最小路径覆盖就好了,每次加入一个待放的球,跑最大流,判断一下,如果它不能归到之前的路径上就让ans++,直到ans>n
注意:codevs上没有SPJ,但要输出方案数!

#include<bits/stdc++.h>
using namespace std;
int n,s,t,tot=1,ans=1;
struct edge
{
    int u,v,w,next;
}e[200000];
int first[3500],dis[3500];
bool sr[3204];
queue<int>q;
void add(int x,int y,int z){e[++tot]=(edge){x,y,z,first[x]};first[x]=tot;}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    q.push(s);dis[s]=1;
    while (!q.empty())
    {
        int k=q.front();
        for (int i=first[k];i;i=e[i].next)
        if (e[i].w&&!dis[e[i].v])
            q.push(e[i].v),dis[e[i].v]=dis[k]+1;
        q.pop();
    }
    return dis[t];
}
int dfs(int x,int maxn)
{
    if (x==t) return maxn;
    int used=0;
    for (int i=first[x];i;i=e[i].next)
        if (dis[e[i].v]==dis[x]+1)
        {
            int k=dfs(e[i].v,min(maxn,e[i].w));
            e[i].w-=k;e[i^1].w+=k;
            used+=k;
            if (used==maxn) return maxn;
        }
    if (!used) dis[x]=0;
    return used;
}
main()
{
    freopen("balla.in","r",stdin);
    freopen("balla.out","w",stdout);
    for (int i=1;i<=56;i++) sr[i*i]=1;
    t=3244;
    scanf("%d",&n);
    for (int i=1;i<=1600;i++,ans++)
    {
        add(s,i,1);add(i,s,0);
        for (int j=1;j<i;j++)
            if (sr[j+i]) add(j,i+1600,1),add(i+1600,j,0);
        add(i+1600,t,1);
        add(t,i+1600,0);
        while (bfs()) ans-=dfs(s,0x7f);
        if (ans>n) {ans=i;break;}
    }
    printf("%d",ans-1);
}

方格取数问题
传送门
思路:二分图染黑白,然后就是求最大权闭合子图了,可以参照上面的太空飞行计划,流量就是点的权值
不过这道题好坑啊,之前的最大流方法总会WA三个点,只能用回最暴力的dfs单线跑流了
代码:

#include<bits/stdc++.h>
#define inf 0x7fffff
#define pd(x,y,z) (x<=y&&y<=z)
using namespace std;
int n,m,s,t,tot=1,sum,ans;
int dis[1000],first[1000],cur[1000],up[1000];
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
queue<int>q;
struct edge
{
    int u,v,w,next;
}e[10000];
void add(int x,int y,int z) {e[++tot]=(edge){x,y,z,first[x]};first[x]=tot;}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    dis[s]=1;q.push(s);
    while (!q.empty())
    {
        int k=q.front();
        for (int i=first[k];i;i=e[i].next)
            if (e[i].w&&!dis[e[i].v])
                q.push(e[i].v),dis[e[i].v]=dis[k]+1;
        q.pop();
    }
    if (dis[t])
        for (int i=s;i<=t;i++) cur[i]=first[i];
    return dis[t];
}
void flow()
{
    int minn=inf;
    for (int i=up[t];i;i=up[e[i].u])
    minn=min(minn,e[i].w);
    ans+=minn;
    for (int i=up[t];i;i=up[e[i].u])
    e[i].w-=minn,e[i^1].w+=minn;
}
void dfs(int x,int maxn)
{
    if (x==t) {flow();return;}
    for (int i=first[x];i;i=e[i].next)
    if (e[i].w&&dis[e[i].v]==dis[x]+1)
    up[e[i].v]=i,dfs(e[i].v,min(maxn,e[i].w));
}
main()
{
    scanf("%d%d",&n,&m);
    int x;
    t=n*m+1;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&x);
            sum+=x;
            if (((i+j)&1))
            {
                add(s,m*(i-1)+j,x);
                add(m*(i-1)+j,s,0);
                for (int k=0;k<4;k++)
                    if(pd(1,i+dx[k],n)&&pd(1,j+dy[k],m))
                        add(m*(i-1)+j,m*(i-1+dx[k])+j+dy[k],inf),
                        add(m*(i-1+dx[k])+j+dy[k],m*(i-1)+j,0);
            }
            else add(m*(i-1)+j,t,x),add(t,m*(i-1)+j,0);
        }
    while (bfs()) dfs(s,inf);
    printf("%d",sum-ans);
}

【多题合集】网络流24题练习(更新至魔术球问题)

标签:

原文地址:http://blog.csdn.net/xym_csdn/article/details/51231871

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!