CandyTime Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1829 Accepted Submission(s): 500 Problem Description There are N candies and M kids, the teacher will give this N candies to the M kids. The i-th kids for the j-th candy has a preference for like[i][j], if he like the sugar, like[i][j] = 1, otherwise like[i][j] = 0. If the i-th kids get the candy which he like Input The Input consists of several cases .The first line contains a single integer t .the number of test cases. For each case starts with a line containing three integers N, M, K (1<=N<=13, 1<=M<=13, 2<=K<=10) The next line contains M numbers which is B[i](0<=B[i]<=1000). Separated by a single space. Then there are M*N like[i][j] , if the i-th kids like the j-th sugar like[i][j]=1 ,or like[i][j]=0. Output If there have a reasonable allocate make every kid feel happy, output "YES", or "NO". Sample Input 2 3 2 2 2 2 0 0 0 0 0 1 3 2 2 2 2 0 0 0 0 0 0 Sample Output Case #1: YES Case #2: NO Hint Give the first and second candy to the first kid. Give the third candy to the second kid. This allocate make all kids happy. |
题意:给N个孩子分配M个糖果。有一个N*M的矩阵表示孩子和糖果的关系,若第i行第j列的数是1则表示第i个孩子喜欢第j个糖果,反之不喜欢。已知,若一个孩子被分配到他喜欢的糖果那么他将获得K的快乐值,反之只能获取1的快乐值。现在给你这N个孩子需要满足的快乐值,问你能不能满足所有孩子的需求。
好经典的建图,无限ORZ大牛。
大牛题解:
由于只要得到糖就肯定有1个快乐度,在这一点上糖的效果是等效的。所以只要考虑有特殊效果的糖的分配就可以了。
当快乐的程度超过b[i]时,多出来的部分就浪费了,为了使浪费尽可能少,我们用费用流加以控制,当获得最大费用最大流的时候,这是的费用的利用率就是最高的。在建图时只需考虑特殊的糖就可以了,建图方法:
源点到每个糖:流为1,费用为0。如果糖对某个人有特殊效果,连边:流为1,费用为0。人向汇点连边:最终快乐的程度不超过b[i]的情况:流为b[i]/k,限制这样的糖的数量,费用为k,因为特殊效果全部利用上了。快乐程度超过b[i]的情况:流为1,限制流量不超过b[i],费用为b[i] % k,因为多出来的快乐无效。当b[i] % k == 0时,这样的边没有必要。当b[i] % k == 1时,这时的糖和普通的糖无异,没必要连边(其实我感觉这里的说法与处理不是很恰当,虽然不影响结果)。
理理思路,说下自己整理后的建图,和大牛的有一点点不同。
建图:设置超级源点source,超级汇点sink,用a表示当前孩子需要满足的快乐值
1,source向每个糖果建边,容量1,费用0;
2,只考虑有特殊的糖,那么对于第i个人喜欢j糖果,则j糖果向第i个人建边,容量为1,费用为0;
3,每个孩子向汇点建边。这时需要分类讨论
【注意a / K 全是整数之间的运算,一定要自己手算】
(1) a % K == 0,表示该孩子选择(a / K)个他喜欢的糖果就可以满足,而且该孩子选择1个他喜欢的糖果,会获取K的快乐值。 建边信息——容量为(a / K),费用为K;
(2) a % K != 0,表示该孩子选择(a / K + 1)个他喜欢的糖果才能满足,这时我们不能只建一条容量为(a / K + 1),费用为K的边,如果这样处理我们就放大了最大流时费用的最大效益,最后得到的快乐值为a - a % K + K > a。因此我们要建一条容量为(a / K),费用为K的边,再建一条容量为1,费用为a % K的边。这样的话在用特殊糖果满足该孩子的需求时,才不会使最后流入汇点的费用增加,最后得到的快乐值为a
- a % K + a % K = a。
建好图,跑一次最大费用最大流。
最终结果:(用sumflow记录所有孩子需要满足的快乐值之和)
最大费用cost——特殊的糖被充分利用后所分配的快乐值之和。若cost >= sumflow 则表示已经满足条件。
最大流flow——为了达到这样的程度使用的糖的数量。
这样就还剩M - flow数目的糖被我们当做普通的糖使用,只要M - flow >= sumflow - cost,就可以满足条件。
AC代码:
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #define MAXN 50 #define MAXM 5000 #define INF 0x3f3f3f3f using namespace std; struct Edge { int from, to, cap, flow, cost, next; }; Edge edge[MAXM]; int head[MAXN], edgenum; int pre[MAXN], dist[MAXN]; bool vis[MAXN]; int N, M, K; int source, sink; int sumflow; void init() { edgenum = 0; memset(head, -1, sizeof(head)); } void addEdge(int u, int v, int w, int c) { Edge E1 = {u, v, w, 0, c, head[u]}; edge[edgenum] = E1; head[u] = edgenum++; Edge E2 = {v, u, 0, 0, -c, head[v]}; edge[edgenum] = E2; head[v] = edgenum++; } void getMap() { source = 0, sink = N + M + 1; int a; sumflow = 0; for(int i = 1; i <= N; i++) { scanf("%d", &a); sumflow += a; addEdge(i, sink, a / K, K);//可以选择a / K个他喜欢的糖果 if(a % K > 1)//保证不会有多余的费用流进入汇点 维护最大流时 费用的最大效益 addEdge(i, sink, 1, a % K); } for(int i = 1; i <= M; i++)//源点从 每个糖果 addEdge(source, i+N, 1, 0); for(int i = 1; i <= N; i++) { for(int j = 1; j <= M; j++) { scanf("%d", &a); if(a)//根据喜欢 的关系 建边 addEdge(j+N, i, 1, 0); } } } bool SPFA(int s, int t) { queue<int> Q; memset(dist, -INF, sizeof(dist)); memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre)); dist[s] = 0; vis[s] = true; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = false; for(int i = head[u]; i != -1; i = edge[i].next) { Edge E = edge[i]; if(dist[E.to] < dist[u] + E.cost && E.cap > E.flow) { dist[E.to] = dist[u] + E.cost; pre[E.to] = i; if(!vis[E.to]) { vis[E.to] = true; Q.push(E.to); } } } } return pre[t] != -1; } void MCMF(int s, int t, int &cost, int &flow) { cost = flow = 0; while(SPFA(s, t)) { int Min = INF; for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { Edge E = edge[i]; Min = min(Min, E.cap-E.flow); } for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { edge[i].flow += Min; edge[i^1].flow -= Min; cost += edge[i].cost * Min; } flow += Min; } } int main() { int t, k = 1; scanf("%d", &t); while(t--) { scanf("%d%d%d", &M, &N, &K); init(); getMap(); int cost, flow; MCMF(source, sink, cost, flow); //跑出的流量是使用特殊糖果的数目 //最大费用 是使用这些糖果得到的最大快乐值 printf("Case #%d: ", k++); if(cost >= sumflow || sumflow - cost <= M - flow) printf("YES\n"); else printf("NO\n"); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。