N个人M个星球,每个人都有适合居住的星球,每个星球有最大的容纳人数。求是否行星能满足住人。
相比二分图求最大匹配,变化在link数组的应用,及对行星人数限制这个条件寻找增广轨,而这里的增广轨即 ”原先已经找好居住行星的人需要找新的行星“ 这一过程。跑一次Hungary即可。注意这里要用c++否则会T。神奇的输入挂可减很多时间
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N = 100005;
int vis[12];
int link[12][N]; //link[i][j]为第i颗行星上的第j个人
int c[15], v[15]; //第I颗行星能容纳的最大人数,当前的人数
int n, m;
int a[N][12]; //mat
int read()
{
char ch = ' ';
int ans = 0;
while( ch < '0' || ch > '9')
ch = getchar();
while(ch <= '9' && ch >= '0')
{
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans;
}
int dfs( int u )
{
for( int i = 0; i < m; i++ )
{
if( !vis[i] && a[u][i] )
{
vis[i] = 1;
if( v[i] < c[i] ) //第I颗行星还能住人
{
link[i][v[i]++] = u; //记录要住的人的编号
return 1;
}
else
{
for( int j = 0; j < v[i]; j++ ) //为已经住在第i颗行星上的人找新行星(有可能重新找的还是原来的行星)
{
if( dfs(link[i][j]) )
{
link[i][j] = u; //腾出j位置给u这个人
return 1;
}
}
}
}
}
return 0;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
memset(v, 0, sizeof(v));
for( int i = 0; i < n; i++ )
for( int j = 0; j < m; j++ )
a[i][j] = read();
for( int i = 0; i < m; i++ )
c[i] = read();
bool OK = 0;
for( int i = 0; i < n; i++ )
{
memset(vis, 0, sizeof(vis));
if( !dfs(i) )
{
OK = 1;
break;
}
}
printf("%s\n", OK ? "NO" : "YES");
}
return 0;
}
邻接表版本的。(时间差在输入那里= =)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N = 100005;
int vis[12];
int link[12][N]; //link[i][j]为第i颗行星上的第j个人
int c[15], v[15]; //第I颗行星能容纳的最大人数,当前的人数
int n, m;
int head[N];
int a[N][12];
struct node{
int to, nxt;
}e[N * 10];
int cnt;
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
memset(v, 0, sizeof(v));
}
int read()
{
char ch = ' ';
int ans = 0;
while( ch < '0' || ch > '9')
ch = getchar();
while(ch <= '9' && ch >= '0')
{
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans;
}
void add(int u, int to )
{
e[cnt].to = to;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
int dfs( int u )
{
for( int i = head[u]; ~i; i = e[i].nxt )
{
int to = e[i].to;
if( !vis[to] )
{
vis[to] = 1;
if( v[to] < c[to] )
{
link[to][v[to]++] = u;
return 1;
}
else
{
for( int j = 0; j < v[to]; j++ )
{
if( dfs(link[to][j]) )
{
link[to][j] = u;
return 1;
}
}
}
}
}
return 0;
}
/*int dfs( int u )
{
for( int i = 0; i < m; i++ )
{
if( !vis[i] && a[u][i] )
{
vis[i] = 1;
if( v[i] < c[i] ) //第I颗行星还能住人
{
link[i][v[i]++] = u; //记录要住的人的编号
return 1;
}
else
{
for( int j = 0; j < v[i]; j++ ) //为已经住在第i颗行星上的人找新行星(有可能重新找的还是原来的行星)
{
if( dfs(link[i][j]) )
{
link[i][j] = u; //腾出j位置给u这个人
return 1;
}
}
}
}
}
return 0;
}*/
int main()
{
while(~scanf("%d%d", &n, &m))
{
int x;
init();
for( int i = 0; i < n; i++ )
for( int j = 0; j < m; j++ )
{
x = read();
//scanf("%d", &x);
if( x )
add(i, j);
}
for( int i = 0; i < m; i++ )
c[i] = read();
//scanf("%d", &c[i]);
bool OK = 0;
for( int i = 0; i < n; i++ )
{
memset(vis, 0, sizeof(vis));
if( !dfs(i) )
{
OK = 1;
break;
}
}
printf("%s\n", OK ? "NO" : "YES");
}
return 0;
}
至于最大流,很明显可以看出来,但是按照 超源 -> 星球 -> 人 ->超汇这种裸建边的话,图大小达到1e6,稳T的。所以我们可以将图缩一下。
因为根据人有1e5,而星球只有10,那么对于人的选择来说,一共有2^10 = 1024个选择,远小于人数,也就是说有很多人的选择是一样的,那么我们利用状压把相同选择的人划到一个集合里面,最后根据这个集合里面的人对对应星球连边即可。
、、终于用最大流过了它哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 10100;
struct node{
int v, w, nxt;
int cap;
}e[N*10];
int head[N];
int dep[N];
int gap[N];
int cur[N];
int pp[N];
int s[N], top;
queue <int> q;
int a[N*10];
int n, m, nn;
int cnt;
int st, ed;
void init()
{
memset( a, 0, sizeof( a ) );
memset( pp, 0, sizeof( pp ) );
memset( head, -1, sizeof( head ) );
cnt = 0;
st = (1<<m)+m, ed = st+1;
}
void add( int u, int v, int w, int rw = 0 )
{
e[cnt].v = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt++;
e[cnt].v = u;
e[cnt].w = 0;
e[cnt].nxt = head[v];
head[v] = cnt++;
}
void rev_bfs( )
{
memset( gap, 0, sizeof( gap ) );
memset( dep, -1, sizeof( dep ) );
while( !q.empty() ) q.pop();
q.push( ed );
dep[ed] = 0;
gap[0] = 1;
while( q.empty() ) {
int u = q.front();
q.pop();
for( int i = head[u]; ~i; i = e[i].nxt ) {
int v = e[i].v;
if( ~dep[v] )
continue;
dep[v] = dep[u] + 1;
gap[dep[u]]++;
q.push( v );
}
}
}
int isap()
{
memcpy( cur, head, sizeof cur );
rev_bfs();
int flow = 0, i, u = st, nv = ed+1;
top = 0;
while( dep[st] < nv ) {
if( u == ed ) {
int tmp, minn = inf;
for( i = 0; i < top; ++i ) {
if( minn > e[s[i]].w ) {
minn = e[s[i]].w;
tmp = i;
}
}
for( i = 0; i < top; ++i ) {
e[s[i]].w -= minn;
e[s[i]^1].w += minn;
}
flow += minn;
top = tmp;
u = e[s[top]^1].v;
}
for( i = cur[u]; ~i; i = e[i].nxt ) {
if( e[i].w > 0 && dep[u] == dep[e[i].v] + 1 ) {
cur[u] = i;
break;
}
}
if( ~i ) {
s[top++] = cur[u];
u = e[i].v;
}
else {
if( 0 == (--gap[dep[u]]) )
break;
int minn = nv;
for( i = head[u]; ~i; i = e[i].nxt ) {
int v = e[i].v;
if( e[i].w > 0 && minn > dep[v] ) {
minn = dep[v];
cur[u] = i;
}
}
dep[u] = minn + 1;
gap[dep[u]]++;
if( u != st ) {
u = e[ s[--top]^1 ].v;
}
}
}
return flow;
}
int read(){//输入优化
char ch = ' ';
while(ch < '0' || ch > '9') ch = getchar();
int x = 0;
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
int main()
{
while( ~scanf("%d%d", &n, &m) )
{
init();
for( int i = 0; i < n; ++i )
{
int x = 0;
for( int j = 0; j < m; ++j )
{
int tt;
//scanf("%d", &tt);
tt = read();
if( tt )
x |= (1 << j);
}
pp[x]++;
}
for( int i = 0; i < ( 1 << m ); ++i )
{
if( pp[i] )
{
add( st, i, pp[i] );
for( int j = 0; j < m; ++j )
{
if( i & ( 1 << j ) )
add( i, j+(1<<m), pp[i] );
}
}
}
for( int i = 0; i < m; ++i )
{
int x;
//scanf("%d", &x);
x = read();
if( x )
add( i+(1<<m), ed, x );
}
int ans = isap();
//printf("%d\n", ans);
if( ans >= n )
puts("YES");
else
puts("NO");
}
return 0;
}