/* 好题~ 网络流不熟啊, 刚开始枚举会被当成喜欢的糖的糖的组合来最大流,确实没有考虑清楚当bi%k的一些不同的情况~ 看了一篇比较有启发的博客后,才写的费用流: 以糖果数量限制流量,在喜欢的糖果与人之间连边 在人与汇点间建费用为k,流量为b[i]/k的边,那么cost*flow就成了glad value了!(这是我茅塞顿开的地方) 那么我在之前用最大流做时不能处理的b[i]%k的问题也好解决了: 当b[i]%k==0时,不用管他, 当b[i]%k==1时,多出来的必选的糖喜不喜欢无所谓,也不用管, 当b[i]%k>1时,我们应尽量去选喜欢的糖,此时加一条边(i,T,1,b[i]%k);就行了。 最后我们求得的最大费用为各人选择喜欢的糖所产生的glad value总值,最大流为选择的喜欢的糖果的数量,所以max_cost+(n-max_flow)>b1+b2+...+bm就是YES了! 当然这里应该求的是最大费用最大流。 */ #include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int NN=100; const int MM=1000; const int INF=0x3fffffff; int n,m,k,sum,cnt,a,S,T,NV,b[NN],like[NN][NN]; struct Edge { int u,v,f,c,next; } e[MM]; int en,head[NN]; void add(int u,int v,int f,int c) { e[en].u=u; e[en].v=v; e[en].f=f; e[en].c=c; e[en].next=head[u]; head[u]=en++; e[en].u=v; e[en].v=u; e[en].f=0; e[en].c=-c; e[en].next=head[v]; head[v]=en++; } void build() { S=0; T=n+m+1; NV=T+1; en=0; for (int i=0; i<NV; i++) head[i]=-1; for (int i=1; i<=n; i++) add(S,i,1,0); for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) if (like[j][i]) add(i,j+n,1,0); for (int j=1; j<=m; j++) { add(j+n,T,b[j]/k,k); if (b[j]%k>1) add(j+n,T,1,b[j]%k); } } bool used[NN]; int dis[NN],p[NN]; bool SPFA(void) { queue<int> q; for (int i=1; i<NV; i++) dis[i]=-INF; dis[S]=0; q.push(S); p[S]=-1; while (!q.empty()) { int u=q.front(); used[u]=false; q.pop(); for (int i=head[u]; i!=-1; i=e[i].next) { int v=e[i].v; if (e[i].f>0 && dis[v]<dis[u]+e[i].c) { dis[v]=dis[u]+e[i].c; p[v]=i; if (!used[v]) { used[v]=true; q.push(v); } } } } if (dis[T]==-INF) return false; else return true; } bool MCMF() { build(); int max_flow=0; int max_cost=0; while (SPFA()) { int u,v; for (v=T; p[v]!=-1; v=u) { u=e[p[v]].u; e[p[v]].f-=1; //新产生的流量总是1 e[p[v]^1].f+=1; } max_flow++; max_cost+=dis[T]; } if (max_cost+n-max_flow>=sum) return true; else return false; } int main() { int T,cas=0; scanf("%d",&T); while (T--) { scanf("%d%d%d",&n,&m,&k); sum=0; for (int i=1; i<=m; i++) { scanf("%d",&b[i]); sum+=b[i]; } for (int i=1; i<=m; i++) for (int j=1; j<=n; j++) scanf("%d",&like[i][j]); printf("Case #%d: ",++cas); if (k<=1) { if (n>=sum) printf("YES\n"); else printf("NO\n"); continue; } if (n>=sum) { printf("YES\n"); continue; } if (n*k<sum) { printf("NO\n"); continue; } if (MCMF()) printf("YES\n"); else printf("NO\n"); } return 0; }