HDU 4292 FOOD 网络流

题目描述:

Description
  You, a part-time dining service worker in your college’s dining hall, are now confused with a new problem: serve as many people as possible.
  The issue comes up as people in your college are more and more difficult to serve with meal: They eat only some certain kinds of food and drink, and with requirement unsatisfied, go away directly.
  You have prepared F (1 <= F <= 200) kinds of food and D (1 <= D <= 200) kinds of drink. Each kind of food or drink has certain amount, that is, how many people could this food or drink serve. Besides, You know there’re N (1 <= N <= 200) people and you too can tell people’s personal preference for food and drink.
  Back to your goal: to serve as many people as possible. So you must decide a plan where some people are served while requirements of the rest of them are unmet. You should notice that, when one’s requirement is unmet, he/she would just go away, refusing any service.

Input
  There are several test cases.
  For each test case, the first line contains three numbers: N,F,D, denoting the number of people, food, and drink.
  The second line contains F integers, the ith number of which denotes amount of representative food.
  The third line contains D integers, the ith number of which denotes amount of representative drink.
  Following is N line, each consisting of a string of length F. �e jth character in the ith one of these lines denotes whether people i would accept food j. “Y” for yes and “N” for no.
  Following is N line, each consisting of a string of length D. �e jth character in the ith one of these lines denotes whether people i would accept drink j. “Y” for yes and “N” for no.
  Please process until EOF (End Of File).

Output
  For each test case, please print a single line with one integer, the maximum number of people to be satisfied.

Sample Input

4 3 3
1 1 1
1 1 1
YYN
NYY
YNY
YNY
YNY
YYN
YYN
NNY 

Sample Output

3 

题目分析:

题意是有n个顾客,每个顾客有喜欢的食品和饮料,你有f种食品,d种饮料,并且有数量限制,求让最多顾客能拿到自己喜欢的食品和饮料的人数。
网络流模板题,POJ3281和这道题几乎一样。建立源点s,汇点t,食品与源点建边,权为数量;饮料与汇点建边,权为数量。顾客点被拆分成食品和饮料,分别与其喜欢的建边,权为1,同时被拆分的顾客点也要连接,这样就形成了一个网络。用网络流的模板方法跑一边就可以。据说这题用所有方法都行。(我用的ISAP)

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 50000;

struct node
{
    int u,v,flow;
    int next;
}mp[MAXN<<5];

int head[MAXN];
int id;
int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];
void init()
{
    id=0;
    memset(head,-1,sizeof(head));
    memset(mp,-1,sizeof(mp));
}
void addedge(int u,int v,int w)
{
    mp[id].u=u;
    mp[id].v=v;
    mp[id].flow=w;
    mp[id].next=head[u];
    head[u]=id++;

    mp[id].u=v;
    mp[id].v=u;
    mp[id].flow=0;
    mp[id].next=head[v];
    head[v]=id++;//双向边,反向边权为0
}

int ISAP(int start,int end,int num)
{
    int news,tmp,Min,ans=0,u,i,v;
    memset(dep,0,sizeof(dep));
    memset(gap,0,sizeof(gap));
    memset(pre,-1,sizeof(pre));
    memcpy(cur,head,sizeof(head));
    gap[0]=num;
    u=start;
    while(dep[start]mp[cur[i]].flow)
                {
                    news=i;
                    Min=mp[cur[i]].flow;
                }
            }
            for(i=start; i!=end; i=mp[cur[i]].v)
            {
                tmp=cur[i];
                mp[tmp].flow-=Min;
                mp[tmp^1].flow+=Min;
            }
            ans+=Min;
            u=news;
        }
        for(i=cur[u]; i!=-1; i=mp[i].next)
        {
            v=mp[i].v;
            if (mp[i].flow && dep[u]==dep[v]+1) break;
        }
        if (i!=-1)
        {
            cur[u]=i;
            pre[v]=u;
            u=v;
        }
        else
        {
            if (0==--gap[dep[u]]) break;
            cur[u]=head[u];
            for(tmp=num+5,i=head[u]; i!=-1; i=mp[i].next)
                if(mp[i].flow)
                    tmp=min(tmp,dep[mp[i].v]);
            dep[u]=tmp+1;
            gap[dep[u]]++;
            if(u!=start)  u=pre[u];
        }
    }
    return ans;
}

int main()
{
    int n,f,d;
    while(~scanf("%d%d%d",&n,&f,&d))
    {
        init();
        int s=0,t=2*n+d+f+1;//源点 汇点
        //0点s点 1~f点为食物(权表示其库存) f+1~f+d为饮料(权表示库存) f+d+1~f+d+n为与食物连着的人(权为0或1) f+d+n+1~f+d+2n为与饮料连着的人(权为0或1) f+d+2n+1为汇点
        for(int i=1; i<=f; i++)
        {
            int x;
            scanf("%d",&x);
            addedge(s,i,x);//将源点与i点连结 权为x
        }
        for(int i=1; i<=d; i++)
        {
            int x;
            scanf("%d",&x);
            addedge(i+f,t,x);//将汇点与i+f点连结 权为x
        }
        for(int i=1; i<=n; i++)
        {
            addedge(f+d+i,f+d+i+n,1);
        }
        char str[MAXN];
        for(int i=1; i<=n; i++)
        {
            scanf("%s",str+1);
            for(int j=1; j<=f; j++)
            {
                if (str[j]=='Y') addedge(j,i+f+d,1);//将客人对food喜好建边
            }
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%s",str+1);
            for(int j=1; j<=d; j++)
            {
                if (str[j]=='Y') addedge(i+f+d+n,f+j,1);//将客人对drink喜好建边
            }
        }
        int ans=ISAP(s,t,t+1);
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(图论)