Codeforces Round #304 (Div. 2) E. Soldier and Traveling 复习一下最大流算法

http://blog.csdn.net/quailty/article/details/46571011
这题是一个比较明显的最大流,但是我试着建了几个图都没有找到很合适的。然后看到了我大q神的博客,瞬间茅塞顿开,在这里膜一波q神Orz

建图:
设置一个源点s ,然后建立s (0)到 1—-n 的边,容量cap为a[i]。 设置一个t,建立1+n — n+n 与 t(2*N+1)之间的路径,容量cap为b[i]。 (从源点到1-n 再到 1+n—n+n 再到t 的最大流图)
对于边 < i,j> 建立边 i—j+n ,容量为inf,再建立 j—–i+n,容量为inf。 表示这两条个城市之间可以相互通行

然后从0–2*n+1 跑一遍最大流,如果得出的最大流结果=a[i]的和=b[i]的和 那么结果就是YES。

主要是自己sb,在记录最终结果的时候脑残了一波,因为最大流不会管你从城市之间的道路走了几遍,他如果有增广路径就必然会走,哪怕这条路会绕来绕去绕好几个城市。 但是这个不会影响最终结果,回顾一下自己写的几个记录:

    ans[x][i]=aa;  //在这个地方记录一下增广的流
    一开始就如果有x到i的流,就直接记录。

    后来写成了:
    if(x<=N){ //x在做  则 i在右
        if(i>N)
            ans[x][i-N]+=aa;
        }
        else{   //x在右,i必定在左
            ans[x-N][i]-=aa;  //这个地方减去
        }
        .....

     现在看看感觉自己好蠢啊
     这样完全是在XJB搞

最后写成:
        ans[x][i]+=aa;
        ans[i][x]-=aa;
        这样就没有问题了,
        输出的时候我们只要
        for(int i=1;i<=N;i++){
           for(int j=1+N;j<=N+N;j++){
               printf("%d ",ans[i][j]);
           }

ac代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define INF 0x7fffffff   //INT_MAX
#define inf 0x3f3f3f3f   //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
template<class T> inline T Min(T a, T b) { return a < b ? a : b; }
template<class T> inline T Max(T a, T b) { return a > b ? a : b;}
using namespace std;
int tab[250][250];//邻接矩阵
int dis[250];//距源点距离,分层图
int q[2000],h,r;  //BFS队列 ,首,尾
int N,M,ANS;//N:点数;M,边数
int a[205],b[205];
int ans[205][205];
int BFS(){
     int i,j;
     memset(dis,-1,sizeof(dis));//以-1填充
     dis[0]=0;
     h=0;r=1;
     q[1]=0;
     while (h<r)
     {
           j=q[++h];
               for (i=0;i<=N+N+1;i++){
                 if (dis[i]<0 && tab[j][i]>0){
                    dis[i]=dis[j]+1;    //说明 i可以作为j的下一层
                    q[++r]=i;
                 }
               }
     }
     if (dis[N+N+1]>0)
        return 1;
     else
        return 0;//汇点的DIS小于零,表明BFS不到汇点
}
//Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广
int find(int x,int low)
{
    int i,aa=0;
    if (x==N+N+1)
        return low;//是汇点
    for (i=0;i<=N+N+1;i++){
         if (tab[x][i] >0   && dis[i]==dis[x]+1    //联通  //是分层图的下一层
            &&(aa=find(i,min(low,tab[x][i]))) )     //
            {
               tab[x][i]-=aa;
               tab[i][x]+=aa;
               //在这个地方记录一下增广的流
               ans[x][i]+=aa;
               ans[i][x]-=aa;

//               printf("操作的两个数:%d %d =%d\n",x,i,aa);
               return aa;
            }
        }
    return 0;

}
int main()
{
    int i,j,f,t,tans;
    //freopen("1.txt","r",stdin);
    while (scanf("%d%d",&N,&M)!=EOF){
       memset(tab,0,sizeof(tab));
       mem(ans);
       int sum_a=0,sum_b=0;
       //以0 为s     2n+1 为 t;
       for(i=1;i<=N;i++){
           scanf("%d",&a[i]);
           tab[0][i]=a[i];
           sum_a+=a[i];
           tab[i][i+N]=a[i];  //inf
       }

       for(i=1+N;i<=N*2;i++){
           scanf("%d",&b[i]);
           sum_b+=b[i];
           tab[i][2*N+1]=b[i];
       }

       for (i=1;i<=M;i++){
           scanf("%d%d",&f,&t);
           tab[f][t+N]=inf;
           tab[t][f+N]=inf;
       }
       if(sum_a!=sum_b){
           printf("NO\n");
           continue;
       }
       ANS=0;
       while (BFS()){   //要不停地建立分层图,如果BFS不到汇点才结束
             while(tans=find(0,inf))
                 ANS+=tans;  //一次BFS要不停地找增广路,直到找不到为止
       }
//           printf("ANS=%d  sum_a=%d\n",ANS,sum_a);
       if(ANS!=sum_a){
           printf("NO\n");
           continue;
       }


       printf("YES\n");
       for(int i=1;i<=N;i++){
           for(int j=1+N;j<=N+N;j++){
               printf("%d ",ans[i][j]);
           }
           printf("\n");
       }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值