JOJ_2453
假如最后剩下的是价值为1的若干颗糖,显然这些糖分给谁都是无所谓的,因此我们不妨先考虑如何分配最终价值为2的这些糖,这样剩下的只能是价值为1的糖再按需分配,即谁还少就给谁就行了。而且我们在分配价值为2的糖的时候,第i个人得到的总价值不应大于B[i],因为这样相当于浪费了糖,即便B[i]为奇数也是一样的。但是如果将糖的价值视作2,这样是没办法做网络流的,因为容量为2的边有可能只流满了一半,于是不妨将价值为2的糖看作价值为1,将B[i]看作B[i]/2,然后将源点连各个人,容量为B[i]/2,再将人连上相对于自己而言价值为2的糖,容量为INF,最后将各个糖和汇点相连,容量为1。这样做完最大流就可以得到最多分配了多少价值为2的糖,再根据剩下了多少价值为1的糖就可以判断最后能否满足每一个人了。
但是这样有个致命的问题就是糖太多了,必然会超时,因此我们要想办法减小糖的规模。由于人数很少,这样就可以根据每颗糖被喜欢的情况将糖划分成不超过1024类,这样图的总点数就比较少了。
#include<stdio.h> #include<string.h> #define MAXN 1500 #define MAXM 15 #define MAXE 33000 #define INF 0x3f3f3f3f int N, M, S, T, h[1030]; int first[MAXN], e, next[MAXE], v[MAXE], flow[MAXE]; int d[MAXN], work[MAXN], q[MAXN]; long long SUM; void add(int x, int y, int z) { v[e] = y, flow[e] = z; next[e] = first[x], first[x] = e ++; } void init() { int i, j, k, a, b, cnt; scanf("%d%d", &N, &M); memset(first, -1, sizeof(first)); memset(h, 0, sizeof(h)); e = cnt = 0; for(i = 1; i <= N; i ++) { a = 0; for(j = 1; j <= M; j ++) { scanf("%d", &k); a = a << 1 | (k == 2); } if(!h[a]) ++ cnt; ++ h[a]; } S = 0, T = cnt + M + 1; cnt = 0; for(i = 1; i < 1024; i ++) if(h[i]) { ++ cnt; for(j = 1; j <= M; j ++) if(i & 1 << (M - j)) add(j, cnt + M, INF), add(cnt + M, j, 0); add(cnt + M, T, h[i]), add(T, cnt + M, 0); } SUM = 0; for(i = 1; i <= M; i ++) { scanf("%d", &b), SUM += b; if(b > 1) add(S, i, b >> 1), add(i, S, 0); } } int bfs() { int i, j, rear = 0; memset(d, -1, sizeof(d[0]) * (T + 1)); d[S] = 0, q[rear ++] = S; for(i = 0; i < rear; i ++) for(j = first[q[i]]; j != -1; j = next[j]) if(flow[j] && d[v[j]] == -1) { d[v[j]] = d[q[i]] + 1, q[rear ++] = v[j]; if(v[j] == T) return 1; } return 0; } int dfs(int cur, int a) { if(cur == T) return a; int t; for(int &i = work[cur]; i != -1; i = next[i]) if(flow[i] && d[v[i]] == d[cur] + 1) if(t = dfs(v[i], a < flow[i] ? a : flow[i])) { flow[i] -= t, flow[i ^ 1] += t; return t; } return 0; } int dinic() { int ans = 0, t; while(bfs()) { memcpy(work, first, sizeof(first[0]) * (T + 1)); while(t = dfs(S, INF)) ans += t; } return ans; } void solve() { if(SUM > N * 2) { printf("No\n"); return ; } if(SUM <= N) { printf("Yes\n"); return ; } printf("%s\n", SUM <= N + dinic() ? "Yes" : "No"); } int main() { int t; scanf("%d", &t); while(t --) { init(); solve(); } return 0; }