很巧妙的建图,没有加成的糖对每个人都是一样的所以建图的时候只考虑加成的情况,以糖给人加的点数为费用,一个人需要的点数为a则建一条由这个人指向汇的容量为a/k费用为-k的边,如果有余数就再建一条容量为1费用为这个余数的边,由源向糖建边,有加成效果的人和糖的组合之间连线,求这个图的最小费用最大流即可。d
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n,m,k,b[15],d[50],size,par[50],ans,cmp;
bool like[15][15];
typedef struct aa
{
int from,to,cap,cost;
aa(int a,int b,int c,int d)
{
from=a;
to=b;
cap=c;
cost=d;
}
aa(){}
} Edge;
Edge edge[10000];
vector<int> G[50];
#define INF 100000000
void input()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
cmp+=b[i];
}
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
int a;
scanf("%d",&a);
if(a) like[i][j]=1;
}
}
void add(int x,int y,int a,int b)
{
edge[size++]=Edge(x,y,a,b);
G[x].push_back(size-1);
edge[size++]=Edge(y,x,0,-b);
G[y].push_back(size-1);
}
void es_map()
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(like[i][j]) add(m+j,i,1,0);
}
for(int i=1;i<=m;i++)
{
if(b[i]==0) continue;
add(i,n+m+1,b[i]/k,-k);
if(b[i]%k>=2) add(i,n+m+1,1,-b[i]%k);
}
for(int j=1;j<=n;j++)
add(0,m+j,1,0);
}
void init()
{
for(int i=0;i<=49;i++) G[i].clear();
cmp=0;
size=0;
ans=0;
memset(like,0,sizeof(like));
}
bool bfs()
{
bool v[50]={0};
queue<int> Q;
Q.push(0);
d[0]=0;
v[0]=1;
for(int i=1;i<=n+m+1;i++) d[i]=INF;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
v[u]=0;
for(int i=0;i<G[u].size();i++)
{
Edge vv=edge[G[u][i]];
if(vv.cap>0&&d[vv.to]>d[u]+vv.cost)
{
d[vv.to]=d[u]+vv.cost;
par[vv.to]=G[u][i];
if(!v[vv.to])
{
Q.push(vv.to);
v[vv.to]=1;
}
}
}
}
return d[n+m+1]!=INF;
}
bool BF()
{
int ans=0,flow=0;
while(bfs())
{
int x=n+m+1,f=INF;
for(int i=x;i!=0;i=edge[par[i]].from)
{
f=min(f,edge[par[i]].cap);
}
flow+=f;
for(int i=x;i!=0;i=edge[par[i]].from)
{
edge[par[i]].cap-=f;
edge[par[i]^1].cap+=f;
ans+=edge[par[i]].cost*f;
}
if(n-flow-ans>=cmp) return 1;
}
return n-flow-ans>=cmp;
}
int main()
{
int t,ca=0;
scanf("%d",&t);
while(t--)
{
init();
input();
es_map();
printf("Case #%d: ",++ca);
if(BF()) printf("YES\n");
else printf("NO\n");
}
return 0;
}