网络流24题 圆桌聚餐

原题位置: http://cogs.sxysxy.org:8080/cogs/problem/problem.php?pid=729

(这个有SPJ)

这个题是一个裸的网络流板子题,都说网络流难在建图,我只能说+1;

这个题的建图方式有两种,但大同小异;

<1> 超级源,超级汇,拆点,拆出的两个点之间为代表数(桌子数),代表和桌子之间的边是1,超级源(汇)与代表(桌子)之间是正无穷;
<2> 超级源,超级汇,超级源(汇)与代表(桌子)之间是代表数(桌子数),代表与桌子之间是1;

(我用的是<1>)

然后跑最大流了,如果最大流等于代表数的话,即所有代表都可以合法的找到桌子,即输出1,否则输出0;

这题描述不清,输出0的话就可以exit(0)了;

至于这道题的一个点就是输出路径(??!路径??!);

就看如果代表和桌子之间的正向边没流量的话,证明该代表选了这张桌子,然后输出就好了;

要是WA了,就开大点数组,然后找个有SPJ的网站,如果还没A,就自行debug吧;

—————————————123——————————————-

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#define II int
#define B bool
#define R register
#define I 1000
using namespace std;


struct node {
    II from,to;
    II flow;
};

vector <node> Q;

vector <II> aa[I*I];

II dis[I], bit[I], kl[I][I], peo[I], tab[I];

II n,m,ans,en,_tot;

void add(R II x,R II y,R II z)
{
    Q.push_back((node) {x,y,z});
    aa[x].push_back(Q.size()-1);
    Q.push_back((node) {y,x,0});
    aa[y].push_back(Q.size()-1);
}


B bfs()
{
    queue <II> op;
    op.push(0);
    dis[0]=1;
    while (!op.empty()) {
        II o=op.front(); op.pop();
        for(R II i=0;i<aa[o].size();i++)
        {
            R node now=Q[aa[o][i]];
            R II go=now.to;
            if(!dis[go]&&now.flow) {
                dis[go]=dis[o]+1;
                op.push(go);
            }
        }
    }
    return dis[en];
}


II dfs(R II x,R II a)
{
    R II f=0,now_flow=0;
    if(!a||x==en) return a;
    for(R II& i=bit[x];i<aa[x].size();i++)
    {
        R node& now=Q[aa[x][i]];
        R II go=now.to;
        if(dis[go]==dis[x]+1&&(f=dfs(go,min(a,now.flow)))>0) {
            now_flow+=f;
            Q[aa[x][i]].flow-=f;
            a-=f;
            Q[aa[x][i]^1].flow+=f;
            if(!a) return now_flow;
        }
    }
    return now_flow;
}


int main()
{
    freopen("roundtable.in","r",stdin);
    freopen("roundtable.out","w",stdout);
//  freopen("1.in","r",stdin);

    scanf("%d%d",&n,&m);
    for(R II i=1;i<=n;i++) scanf("%d",&peo[i]), _tot+=peo[i];
    for(R II i=1;i<=m;i++) scanf("%d",&tab[i]);

    en=n+n+m+m+1;
    for(R II i=1;i<=n;i++) add(0,i,123456789);
    for(R II i=n+n+m+1;i<=n+n+m+m;i++) add(i,en,123456789);

    for(R II i=1;i<=n;i++) add(i,n+i,peo[i]);
    for(R II i=n+n+1;i<=n+n+m;i++) add(i,m+i,tab[i-n-n]);

    for(R II i=n+1;i<=n+n;i++)
    {
        for(R II j=n+n+1;j<=n+n+m;j++) 
          add(i,j,1);
    }

    while (bfs()) {
        for(R II i=0;i<=en;i++) bit[i]=0;
        ans+=dfs(0,123456789);
        for(R II i=0;i<=en;i++) dis[i]=0;
    }

    ans==_tot? printf("1\n"): printf("0\n");

    if(ans==_tot) {
        for(R II i=n+1;i<=n+n;i++)
        {
            for(R II j=0;j<aa[i].size();j++)
            {
                if(!Q[aa[i][j]].flow) printf("%d ",Q[aa[i][j]].to-n-n);
                // 找路径,如果该正向边没流量的话,输出;
            }
            printf("\n");
        }
    }

    exit(0);
} 

————————————–456———————————————

by pretend-fal

END;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值