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
he will get K glad value. If he or she do not like it. He will get only one glad value. We know that the i-th kids will feel happy if he the sum of glad values is equal or greater than B[i]. Can you tell me whether reasonable allocate this N candies, make
every kid feel happy.
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
Sample Output
|
题意:给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;
}