感觉像最大流的题,就是想不出怎么建图~~
搜题解后发现建图思想太巧妙了:
1 - 源点0到1~N个糖果,容量为1,费用为0
2 - 根据like数组,like[i][j] == 1时在糖果j和人N+i之间建立有一条边,容量为1,费用为0
3*- 根据b[i]和K的值建立小孩和汇点之间的边:
如果b[i] 是 K 的倍数, 说明花费b[i] / K个喜欢的糖果可以达到b[i],建立一条边,费用为K,容量为b[i] / K;
否则,将这条边拆为两部分,第一部分是b[i] / K的部分,第二部分根据b[i] % K的部分。(如果b[i] % k == 0,说明b[i]是k的倍数;
若b[i] % k == 1, 特殊糖果和一般糖果价值一样,没必要当做特殊糖果处理)
建好图后,求最大费用最大流(只需将费用改为负的,然后套最小费用最大流即可).。得出特殊糖果匹配b[i]的最大值。看剩余的普通糖果是否满足缺少的b[i]。
表达能力不强,只能说到这程度了~~另外很诡异的是第一次程序进入死循环,提交后竟然a了。不理解啊不理解~~有兴趣的可以看下,求指教~~
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<queue>
#include<algorithm>
using namespace std;
#define MAXN 1200
int B[22];
int like[22][22];
typedef struct
{
int from, to, cap, flow, cost;
int next;
}Node;
int src, tink, N, M, K, cnt, f;
int head[MAXN];
Node node[MAXN*2];
int dis[MAXN];
int pre[MAXN];
int re[MAXN];
int inq[MAXN];
void init()
{
src = 0;
tink = 1+M+N;
cnt = 2;
memset(node, 0, sizeof(node));
memset(head, -1, sizeof(head));
}
void addEages(int u, int v, int cap, int cost)
{
node[cnt].from = u;
node[cnt].to = v;
node[cnt].cap = cap;
node[cnt].cost = cost;
node[cnt].next = head[u];
head[u] = cnt++;
node[cnt].from = v;
node[cnt].to = u;
node[cnt].cap = 0;
node[cnt].cost = -cost;
node[cnt].next = head[v];
head[v] = cnt++;
}
int MinCostMaxFlow(int s, int t)
{
int i, u, v, ind, cap, cost, c = 0;
f = 0;
queue<int> q;
for(;;)
{
for(i = s; i <= t; i++)
{
dis[i] = INT_MAX;
}
dis[s] = 0;
memset(inq, 0, sizeof(inq));
q.push(s);
inq[s] = 1;
pre[s] = s;
while(!q.empty())
{
u = q.front();
q.pop();
inq[u] = 0;
//printf("%d\n", u);
ind = head[u];
for(; ind != -1; ind = node[ind].next) //把这个地方的-1改为0,会进入死循环,但是提交也会ac,不理解
{
cap = node[ind].cap;
cost = node[ind].cost;
u = node[ind].from;
v = node[ind].to;
// printf("%d\n", v);
if(cap > 0 && dis[v] > dis[u] + cost)
{
dis[v] = dis[u] + cost;
pre[v] = u;
re[v] = ind;
if(!inq[v])
{
inq[v] = 1;
q.push(v);
}
}
}
}
if(dis[t] == INT_MAX)
break;
for(u = t; u != s; u = pre[u])
{
node[re[u]].cap -= 1;
node[re[u]^1].cap += 1;
}
f += 1;
c += dis[t];
}
return c;
}
int main()
{
int tcases, i, j, sumb, tt;
scanf("%d", &tcases);
tt = tcases;
while(tcases--)
{
scanf("%d%d%d", &N, &M, &K);
sumb = 0;
init();
for(i = 1; i <= M; i++)
{
scanf("%d", &B[i]);
sumb += B[i];
addEages(N+i, tink, B[i]/K, -K);
if(B[i] % K > 1)
addEages(N+i, tink, 1, -B[i]%K);
}
for(i = 1; i <= M; i++)
{
for(j = 1; j <= N; j++)
{
scanf("%d", &like[i][j]);
if(like[i][j])
{
addEages(j, i+N, 1, 0);
}
}
}
for(i = 1; i <= N; i++)
{
addEages(src, i, 1, 0);
}
/*for(i = 2; i < cnt; i++)
{
printf("%d\n", node[i].from);
int tmp = head[node[i].from];
while(tmp != -1)
{
printf("%d ", node[tmp].to);
tmp = node[tmp].next;
}
cout << endl;
}*/
printf("Case #%d: ", tt-tcases);
int ans = MinCostMaxFlow(src, tink);
if(sumb + ans <= N - f)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}