BZOJ 3894&1497&1711&3438 网络流入门题

3894:矩形,每个点选文科一个得分,理科一个得分,一个点上下左右都和这个点选的一样会有额外得分。求最大得分。
最小割。把所有权值加上,s连每个点,每个点连t,流量均为得分。全选s的特殊得分建一个点,s连它,流量为得分,它连所有包含的点,流量无穷(意为这个关系不可割断)。全选t的同理。
这样所有矛盾关系都对应一条从s到t的通路,最小割即可。
注意dinic增广过程中把一个点设成-1的条件是分配到这个点的流量有剩余,那么这个点肯定流不了了。否则,可能这个点还有增流的能力。

#include<cstdio>
#define gm 101
using namespace std;
struct edge
{
    int to,flow;
}table[300000];
int head[50000],nex[300000],tot=1;
inline void add(int x,int y,int f)
{
    ++tot;
    table[tot].to=y;
    table[tot].flow=f;
    nex[tot]=head[x];
    head[x]=tot;
}
inline void link(int x,int y,int f)
{
    add(x,y,f);
    add(y,x,0);
}
const int inf=0x7fffffff;
int s=0,t;
int n,m,v,top;
int pos[gm][gm],arts[gm][gm],sciences[gm][gm];
int x[]={1,-1,0,0,0},y[]={0,0,1,-1,0};
int ans=0;
int d[50000];
#include<queue>
#include<cstring>
#include<algorithm>
inline bool bfs()
{
    static queue<int> q;
    memset(d,-1,sizeof d);
    d[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(int i=head[now];i;i=nex[i])
        {
            edge& e=table[i];
            if(e.flow&&d[e.to]==-1)
            d[e.to]=d[now]+1,q.push(e.to);
        }
    }
    return d[t]!=-1;
}
int send(int p,int flow)
{
    if(p==t) return flow;
    int last=flow,kre;
    for(int i=head[p];i;i=nex[i])
    {
        int to=table[i].to;
        if(!table[i].flow||d[to]!=d[p]+1) continue;
        last-=kre=send(to,min(last,table[i].flow));
        if(!kre) d[to]=-1;
        table[i].flow-=kre;
        table[i^1].flow+=kre;
        if(!last) break;
    }
    if(last) d[p]=-1;
    return flow-last;
}
inline int dinic()
{
    int sum=0;
    while(bfs())
    sum+=send(s,inf);
    return sum;
}
int main()
{
    scanf("%d%d",&n,&m);
    t=n*m*3+1;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        pos[i][j]=++top;
        scanf("%d",&v);
        ans+=v;
        link(s,pos[i][j],v);
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        scanf("%d",&v);
        ans+=v;
        link(pos[i][j],t,v);
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        arts[i][j]=++top;
        scanf("%d",&v);
        ans+=v;
        link(s,arts[i][j],v);
        for(int k=0;k<5;++k)
        {
            int xx=i+x[k],yy=j+y[k];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m)
            link(arts[i][j],pos[xx][yy],inf);
        }
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        sciences[i][j]=++top;
        scanf("%d",&v);
        ans+=v;
        link(sciences[i][j],t,v);
        for(int k=0;k<5;++k)
        {
            int xx=i+x[k],yy=j+y[k];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m)
            link(pos[xx][yy],sciences[i][j],inf);
        }
    }
    printf("%d\n",ans-dinic());
    return 0;
}

1497:选用户有一个收入,选电站有一个花费,选某个用户就必须选指定的电站。
S->用户->电站->T,最小割即可。

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
int s=0,t;
const int inf=0x7fffffff;
struct e
{
    int t,flow;
    e *n,*r;
    e(int t,int flow,e *n):t(t),flow(flow),n(n){}
}*f[55005];
inline void link(int a,int b,int flow)
{
    e *x=f[a]=new e(b,flow,f[a]),*y=f[b]=new e(a,0,f[b]);
    x->r=y,y->r=x;
}
int p,a,b,c;
int d[55005];
inline bool bfs()
{
    memset(d+1,-1,n+m+1<<2);
    static queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(e *i=f[now];i;i=i->n)
        if(i->flow&&d[i->t]==-1)
        d[i->t]=d[now]+1,q.push(i->t);
    }
    return d[t]!=-1;
}
int send(int x,int maxf)
{
    if(x==t) return maxf;
    int tot=0;
    for(e *i=f[x];i;i=i->n)
    if(i->flow&&d[i->t]==d[x]+1)
    {
        int kre=send(i->t,min(maxf,i->flow));
        tot+=kre;
        if(!kre) d[i->t]=-1;
        i->flow-=kre;
        i->r->flow+=kre;
        if(!(maxf-=kre)) break;
    }
    return tot;
}
inline int dinic()
{
    int ans=0;
    while(bfs()) 
    ans+=send(s,inf);
    return ans;
}
int ans=0;
int main()
{
    scanf("%d%d",&n,&m);
    t=n+m+1;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&p);
        link(i,t,p);
    }
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        ans+=c;
        link(s,i+n,c);
        link(i+n,a,inf);
        link(i+n,b,inf);
    }
    printf("%d\n",ans-dinic());
    return 0;
}

1711:每一头牛可以吃一些种食物和一些饮料,现有一些食物和饮料,每种一个,问最多有多少牛可以得到能吃的饮料和食物各一种
s->饮料->牛->食物->t,要注意的是每头牛最多只能被满足一次,因此将每头牛拆成两个点,中间连一条流量1的边,就限制住了流量。
数组切记不要开小。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct edge
{
    int to,flow,nex;
}table[10000];
int head[1000],top=1;
inline void add(int x,int y,int f)
{
    table[++top].to=y;
    table[top].flow=f;
    table[top].nex=head[x];
    head[x]=top;
}
inline void link(int x,int y,int f)
{
    add(x,y,f);
    add(y,x,0);
}
int n,f,d;
int s=0,t;
int a,b,v;
int dep[1000];
inline bool bfs()
{
    static queue<int> q;
    memset(dep,-1,sizeof dep);
    dep[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(int i=head[now];i;i=table[i].nex)
        {
            int to=table[i].to;
            if(table[i].flow&&dep[to]==-1)
            dep[to]=dep[now]+1,q.push(to);
        }
    }
    return dep[t]!=-1;
}
int send(int x,int flow)
{
    if(x==t) return flow;
    int last=flow,kre;
    for(int i=head[x];i&&last;i=table[i].nex)
    {
        int to=table[i].to;
        if(!table[i].flow||dep[to]!=dep[x]+1) continue;
        last-=kre=send(to,min(last,table[i].flow));
        table[i].flow-=kre;
        table[i^1].flow+=kre;
    }
    if(last) dep[x]=-1;
    return flow-last;
}
inline int dinic()
{
    int sum=0;
    while(bfs())
    sum+=send(s,0x7fffffff);
    return sum;
}
int main()
{
    scanf("%d%d%d",&n,&f,&d);
    t=f+d+n+n+1;
    for(int i=1;i<=f;++i)
    link(s,i,1);
    for(int i=1;i<=d;++i)
    link(f+i,t,1);
    int now=f+d+1;
    for(int i=1;i<=n;++i)
    {
        link(now,now+1,1);
        scanf("%d%d",&a,&b);
        while(a--)
        {
            scanf("%d",&v);
            link(v,now,1);
        }
        while(b--)
        {
            scanf("%d",&v);
            link(now+1,f+v,1);
        }
        now+=2;
    }
    printf("%d\n",dinic());
    return 0;
}

3438:每种作物种到A一个收益,种到B一个收益,有一些组合,种到A一个收益,种到B一个收益,求最大收益。
和第一题一样。依旧注意数组不要开小。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct e
{
    int t,f;
    e *n,*r;
    e(int t,int f,e *n):t(t),f(f),n(n){}
}*f[10000];
int n,m,v;
const int s=0,t=9999,inf=0x7fffffff;
inline void link(int x,int y,int flow)
{
    f[x]=new e(y,flow,f[x]);
    f[y]=new e(x,0,f[y]);
    f[x]->r=f[y];
    f[y]->r=f[x];
}
unsigned long long ans=0;
int d[10000];
inline bool bfs()
{
    static queue<int> q;
    memset(d,-1,sizeof d);
    d[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(e *i=f[now];i;i=i->n)
        if(i->f&&d[i->t]==-1)
        d[i->t]=d[now]+1,q.push(i->t);
    }
    return d[t]!=-1;
}
int send(int now,int flow)
{
    if(now==t) return flow;
    int last=flow;
    for(e *i=f[now];i&&last;i=i->n)
    if(i->f&&d[i->t]==d[now]+1)
    {
        int kre=send(i->t,min(last,i->f));
        last-=kre;
        i->f-=kre;
        i->r->f+=kre;
    }
    if(last) d[now]=-1;
    return flow-last;
}
unsigned long long dinic()
{
    unsigned long long sum=0;
    while(bfs())
    sum+=send(s,inf);
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&v);
        link(s,i,v);
        ans+=v;
    }
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&v);
        link(i,t,v);
        ans+=v;
    }
    scanf("%d",&m);
    int tot=n+1;
    for(int i=1;i<=m;++i,tot+=2)
    {
        int k;scanf("%d",&k);
        scanf("%d",&v),link(s,tot,v),ans+=v;
        scanf("%d",&v),link(tot+1,t,v),ans+=v;
        while(k--)
        scanf("%d",&v),link(tot,v,inf),link(v,tot+1,inf);
    }
    printf("%llu\n",ans-dinic());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值