网络流 (HDU - 3605)

加超级源点和汇点的思想,并且利用二进制的特点把n个人划分到 1----1024 之间这样就能够划出每个人的特异性,由于在操作中开始是限定了起点的流量的,所以(i&num[j])则是不考虑限流的把这些人放回m个终点,再由m个终点汇聚到超级汇点.

参考模板:http://www.cnblogs.com/wally/archive/2013/05/03/3054778.html

#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <list>
#include <deque>
#include <set>
#include <vector>
#include <map>
#define LL long long
#define max(a,b) (a>b?a:b)
using namespace std;
const int MAXN=1e5+100;
const int MAXM=4e6+100;
const int MAX=0x7fffffff;
struct node
{
    int to,nex,val;
}edge[MAXN];
int level[MAXN];///标记层次
///间隙优化,定义gap[i]为标号是i的点的个数
///在重标记i时,检查gap[level[i]],若减为0,这算法结束。
int gap[MAXN];
int pre[MAXN];
int cur[MAXN];
int head[MAXN];
int NV,NE;///点 边
void inte()
{
    NE=0;
    memset(head,-1,sizeof(head));

}
///NE 记录边数
void  addedge(int fr,int to,int val,int cap=0)
{
    edge[NE].to=to;edge[NE].val=val;edge[NE].nex=head[fr];
    head[fr]=NE++;///向边

    edge[NE].to=fr;edge[NE].val=cap;edge[NE].nex=head[to];
    head[to]=NE++;///反边
}

int sap(int start,int ed)
{
    memset(level,0,sizeof(level));
    memset(pre,-1,sizeof(pre));
    memset(gap,0,sizeof(gap));
    ///cur保存的是当前弧
    memcpy(cur,head,sizeof(head));
    int u=pre[start]=start;//原点本身为前驱
    int maxflow=0,aug=-1;
    gap[0]=NV;
    while(level[start]<NV)
    {
        loop:
            for(int &i=cur[u];i!=-1;i=edge[i].nex)
            {
               //int now=edge[i].to;
               int v=edge[i].to;
               if(edge[i].val&&level[u]==level[v]+1)
               {
                   if(aug==-1)
                   {
                       aug=edge[i].val;
                   }
                   else
                   {
                       aug=min(edge[i].val,aug);
                   }
                   pre[v]=u;
                   u=v;
                   if(v==ed)
                   {
                       maxflow+=aug;
                       for(u=pre[v];v!=start;v=u,u=pre[v])
                       {
                        edge[cur[u]].val-=aug;
                        edge[cur[u]+1].val+=aug;
                       }
                       aug=-1;
                   }
                   goto loop;
               }

            }
        int minlevel=NV;
        for(int i=head[u];i!=-1;i=edge[i].nex)
        {
            int v=edge[i].to;
            if(edge[i].val&&minlevel>level[v])
            {
                    cur[u]=i;///记录下有效边的位置
                    minlevel=level[v];
            }
        }

        gap[level[u]]--;
        if(gap[level[u]]==0)break;
    level[u]=minlevel+1;
    gap[level[u]]++;
    u=pre[u];

    }
return maxflow;
}
int cnt[MAXN]={0};
int num[13];
int main()
{
    ios::sync_with_stdio(false);

    int n,m;
    num[0]=1;
    for(int i=1;i<=10;i++)
    {
        num[i]=num[i-1]*2;
    }
    while(cin>>n>>m)
    {

    inte();
    memset(cnt,0,sizeof(cnt));
    //NV=m;
    NV=1024+m+2;
    int fr,to,val;

    for(int i=0;i<n;i++)
    {

        int tmp=0;
        for(int j=0;j<m;j++)
        {
            cin>>val;
            if(val)//cnt[i*num[j]]++;
            {
                tmp+=val*num[j];
            }
        }
        cnt[tmp]++;
    }
    for(int i=0;i<1024;i++)
    {
        if(cnt[i]==0)continue;
        addedge(0,i+1,cnt[i]);
       for(int j=0;j<10;j++)
        {
           /* if(i&num[j])
            {
                cout<<"cnt= "<<cnt[i]<<endl;
                cout<<"i= "<<i<<" num[j]= "<<num[j]<<endl;
            }*/
            if(i&num[j])addedge(i+1,1024+j+1,MAXM);//把1024条边不限量的还回m个汇点
        }
    }
   for(int i=1;i<=m;i++)//把m个汇点汇到超级汇点1024+m+1处
   {
     cin>>val;//num[i];
     addedge(1024+i,1024+m+1,val);
   }
   /*
   for(int i=1;i<=m;i++)
   {
       if(cnt[i]!=0)addedge(0,i,cnt[i]);
   }*/
    int ans=sap(0,1024+m+1);//起点为0
   // cout<<ans<<endl;
   if(ans==n)
    cout<<"YES"<<endl;
    else cout<<"NO"<<endl;

    }
    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值