牛是很挑食的。每头牛都偏爱特定的食物和饮料,其他的就不吃了。

农夫约翰为他的牛做了美味的饭菜,但他忘了根据它们的喜好检查菜单。虽然他不可能喂饱所有的人,但他想让尽可能多的奶牛吃上一顿有食物和水的大餐。

农民John煮了F(1≤F≤100)种食物,准备了D(1≤D≤100)种饮料。他的每头牛(1≤N≤100)已经决定了她是吃某种食物还是喝某种饮料。农民约翰必须为每头奶牛分配一种食物和一种饮料,以使得到这两种食物的奶牛数量最大化。

每一道菜或饮料只能由一头奶牛食用。,一旦食物2型被分配给一头奶牛,其他奶牛就不能被分配食物2型。

输入

第1行:三个用空格分隔的整数:N、F和D

行2 . .N+1:每一行i以两个整数Fi和Di开头,分别是我喜欢的菜的数量和我喜欢的饮料的数量。下一个Fi整数表示我要吃的牛的盘子,后面的Di整数表示我要喝的牛的饮料。

输出

第1行:一个单一的整数,它是符合奶牛愿望的能被喂养的食物和饮料的最大数量

题意:

每一头牛有它所喜欢吃的东西和喝的东西,现在你有f种食物,d种饮料。现在你应该去用有限的食物和饮料去服务更多的牛。只有你给一头牛了一个它喜欢吃的食物和一瓶它喜欢喝的饮料,然后才算作服务了它

对于每一种牛,如果它有多种喜欢的食物,你只需要从中挑取一个就可以,不需要全部都给它安排上;饮料也是这样

题解:

st为最大流的起点,en为最大流的终点

这一道题刚拿到手确实不知道怎么用最大流来做。。。。

你可以让每一种食物和起点st建一条边,而且很明显这一条边的容量肯定是1,因为每一个食物只能给一个牛。同样让饮料和终点en建一条边。容量同样为1

这个时候还需要对n头牛进行拆点成i->start,i->last,然后对于牛喜欢吃的食物,就让食物和i->start建一条容量为1的边。对于牛喜欢喝的饮料就让i->last和饮料建一条容量为1的边

拆点的话就要在i->start和i->last之间建一条容量为1的边,用来限制每一头牛只能服务一次。如果不拆点的话可能会导致一头牛被服务了多次

毕竟建图方式不一样,有的时候拆不拆点不一定,因为我的这种方式建图如果不拆点的话一头牛就可能被服务多次

比如牛喜欢吃1 2 两个食物,喝3,4两种饮料,这样的话不拆点1可以直接通过牛和3或4相连,从而使结果加1.之后2食物还可以通过牛找另一个饮料是结果再加1.

但是如果拆点的话,因为牛与牛之间还连了一条容量为1的边,所以每一个牛就只能通过一次

当然每一条边还需要建一条它的反向边,容量为0.这条边的作用就是为了反悔。因为之前可能某物品用了这条边,但是在后面在找最有结果的时候可能这个物品不使用这条边的才是最优。这个时候就需要反向边

代码:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<queue>
  6 using namespace std;
  7 const int maxn=10000;
  8 const int INF=0x3f3f3f3f;
  9 int head[maxn],cnt,st,en,dis[maxn],cur[maxn];
 10 struct edge
 11 {
 12     int v,next,c,flow;
 13 }e[maxn];
 14 void add_edge(int x,int y,int z)
 15 {
 16     e[cnt].v=y;
 17     e[cnt].c=z;
 18     e[cnt].flow=0;
 19     e[cnt].next=head[x];
 20     head[x]=cnt++;
 21 }
 22 bool bfs()
 23 {
 24     memset(dis,0,sizeof(dis));
 25     dis[st]=1;
 26     queue<int>r;
 27     r.push(st);
 28     while(!r.empty())
 29     {
 30         int x=r.front();
 31         r.pop();
 32         for(int i=head[x];i!=-1;i=e[i].next)
 33         {
 34             int v=e[i].v;
 35             if(!dis[v] && e[i].c>e[i].flow)
 36             {
 37                 dis[v]=dis[x]+1;
 38                 r.push(v);
 39             }
 40         }
 41     }
 42     return dis[en];
 43 }
 44 int dinic(int s,int limit)
 45 {
 46     if(s==en || !limit) return limit;
 47     int ans=0;
 48     for(int &i=cur[s];i!=-1;i=e[i].next)
 49     {
 50         int v=e[i].v,feed;
 51         if(dis[v]!=dis[s]+1) continue;
 52         feed=dinic(v,min(limit,e[i].c-e[i].flow));
 53         if(feed)
 54         {
 55             e[i].flow+=feed;
 56             e[i^1].flow-=feed;
 57             limit-=feed;
 58             ans+=feed;
 59             if(limit==0) break;
 60         }
 61     }
 62     if(!ans) dis[s]=-1;
 63     return ans;
 64 }
 65 int main()
 66 {
 67     memset(head,-1,sizeof(head));
 68     int n,f,d;
 69     scanf("%d%d%d",&n,&f,&d);
 70     st=0;
 71     en=2*n+f+d+1;
 72     for(int i=1;i<=f;++i)
 73     {
 74         add_edge(st,2*n+i,1);
 75         add_edge(2*n+i,st,0);
 76     }
 77     for(int i=1;i<=d;++i)
 78     {
 79         add_edge(2*n+f+i,en,1);
 80         add_edge(en,2*n+f+i,0);
 81     }
 82     for(int i=1;i<=n;++i)
 83     {
 84         add_edge(i,n+i,1);
 85         add_edge(n+i,i,0);
 86         int sum1,sum2;
 87         scanf("%d%d",&sum1,&sum2);
 88         int x;
 89         for(int j=0;j<sum1;++j)
 90         {
 91             scanf("%d",&x);
 92             add_edge(x+2*n,i,1);
 93             add_edge(i,x+2*n,0);
 94         }
 95         for(int j=0;j<sum2;++j)
 96         {
 97             scanf("%d",&x);
 98             add_edge(n+i,x+f+2*n,1);
 99             add_edge(x+f+2*n,n+i,0);
100         }
101     }//主函数从开头到这就是建图
102
103     int ans=0;
104     while(bfs())
105     {
106         for(int i=0;i<=en;i++)
107             cur[i]=head[i];
108         ans+=dinic(st,1);  //这个1也可以改成无穷大
109     }
110     printf("%d\n",ans);
111     return 0;
112 }
View Code
02-01 01:20