题意:有n个人,m个星球,要搬家,一些人有特定喜欢的星球,让尽量多的人到星球。
输入n,m
输入n*m矩阵表示 i 人是否喜欢 j 星球
输入m个数字表示星球对人的容量
直接建图会超时,然后我们发现m最大是10,所以每个人对星球的选择最多为2^10种,所以肯定有很多人的选择是一样的,所以建图的人点改为选择的情况。
Dinic TLE,贴个SAP模板就过了。。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false)
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define NMAX 10002
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
const int inf = 0x3f3f3f3f;
const int maxn = 1044; //2^10+10+2=1036
const int maxm = 12000; //2^10+2^10*10+10=11274
struct Edge {
int to, next, cap, flow;
} edge[maxm*2]; //注意是MAXM
int tol;
int head[maxn];
int gap[maxn], dep[maxn], cur[maxn];
void init() {
tol = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w, int rw = 0) {
edge[tol].to = v;
edge[tol].cap = w;
edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;
edge[tol].cap = rw;
edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
int Q[maxn];
void BFS(int start, int end) {
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof(gap));
gap[0] = 1;
int front = 0, rear = 0;
dep[end] = 0;
Q[rear++] = end;
while (front != rear) {
int u = Q[front++];
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (dep[v] != -1) {
continue;
}
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[maxn];
int sap(int start, int end, int N) {
BFS(start, end);
memcpy(cur, head, sizeof(head));
int top = 0;
int u = start;
int ans = 0;
while (dep[start] < N) {
if (u == end) {
int Min = inf;
int inser;
for (int i = 0; i < top; i++)
if (Min > edge[S[i]].cap - edge[S[i]].flow) {
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
for (int i = 0; i < top; i++) {
edge[S[i]].flow += Min;
edge[S[i] ^ 1].flow -= Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ 1].to;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u]) {
flag = true;
cur[u] = i;
break;
}
}
if (flag) {
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) {
return ans;
}
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != start) {
u = edge[S[--top] ^ 1].to;
}
}
return ans;
}
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int a[maxn];
int main() {
int n, m;
// RE
while (~scanf("%d%d", &n, &m)) {
CLR(a, 0);
init();
int ss = 0, tt = (1 << m) + m + 1; //人:1~1<<m 星球:(1<<m)+1~(1<<m)+m 终点:(1<<m)+m+1
FOR(i, 1, n) {
int tmp = 0;
FOR(j, 0, m - 1) {
int w = read();
if (w) {
tmp |= (1 << (j));
}
}
a[tmp]++;
}
FOR(j, 1, m) {
int w = read();
addedge((1 << m) + j, tt, w); //星球与终点
}
for (int i = 0; i < (1 << m); i++) { //遍历人的情况,0到(1<<m)-1
if (a[i]) {
addedge(ss, 1 + i, a[i]); //起点与人
for (int j = 0; j < m; j++) {
if (i & (1 << j)) { //如果i里的j位是1就说明与j星球有边
addedge(i + 1, (1 << m) + j + 1, a[i]); //人和星球建边
}
}
}
}
if (sap(ss,tt,tt) >= n) {
puts("YES");
} else {
puts("NO");
}
}
return 0;
}
TLE
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false)
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define NMAX 10002
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
const int inf=0x3f3f3f3f;
const int maxn=1044; //2^10+10+2=1036
const int maxm=12000; //2^10+2^10*10+10=11274
int head[maxn],dis[maxn],cnt,ss,tt;
struct Edge
{
int v,w,next;
}edge[maxm*2];
int a[maxn];
void add(int u,int v,int w)
{
edge[cnt].v=v,edge[cnt].w=w,edge[cnt].next=head[u],head[u]=cnt++;
edge[cnt].v=u,edge[cnt].w=0,edge[cnt].next=head[v],head[v]=cnt++;
}
void init()
{
CLR(head,-1);
cnt=0;
}
int bfs()
{
queue<int>q;
q.push(ss);
CLR(dis,-1);
dis[ss]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(edge[i].w && dis[v]<0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[tt]>0;
}
int dfs(int u,int tmin)
{
int flow;
if(u==tt) return tmin;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(edge[i].w &&
(dis[v]==dis[u]+1) &&
(flow=dfs(v,min(tmin,edge[i].w)))
){
edge[i].w-=flow;
edge[i^1].w+=flow;
return flow;
}
}
// dis[u]=-1;
return 0;
}
int maxFlow()
{
int ans=0,tans=0;
while(bfs())
{
while(tans=dfs(ss,inf))
ans+=tans;
}
return ans;
}
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main()
{
int n,m;
// RE
while(~scanf("%d%d",&n,&m))
{
CLR(a,0);
init();
ss=0,tt=(1<<m)+m+1; //人:1~1<<m 星球:(1<<m)+1~(1<<m)+m 终点:(1<<m)+m+1
FOR(i,1,n)
{
int tmp=0;
FOR(j,0,m-1)
{
int w=read();
if(w)
tmp|=(1<<(j));
}
a[tmp]++;
}
FOR(j,1,m)
{
int w=read();
add((1<<m)+j,tt,w); //星球与终点
}
for(int i=0;i<(1<<m);i++) //遍历人的情况,0到(1<<m)-1
{
if(a[i])
{
add(ss,1+i,a[i]); //起点与人
for(int j=0;j<m;j++)
{
if(i&(1<<j)) //如果i里的j位是1就说明与j星球有边
{
add(i+1,(1<<m)+j+1,a[i]); //人和星球建边
}
}
}
}
if(maxFlow()>=n)
puts("YES");
else
puts("NO");
}
return 0;
}