n个人类移民到m个外星,每个人都有喜欢的星星,但星星有最大人数限制,问能不能,让每个人都移民到喜欢的星球
可用最大流也可用二分图最大匹配
匈牙利算法的一个变形,把一对一变成一对多
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define maxn 100005
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007
int n, m;
int link[maxn][15], star[15], sta[15][maxn], used[15], have[15];
bool find(int x) {
for (int i = 1; i <= m; i ++) {
if(!link[x][i] || used[i]) continue;//如果你不喜欢或者前面已经的人已经选过了
used[i] = 1;
if(have[i] < star[i]) {
sta[i][++have[i]] = x;
return 1;
}else{
for (int j = 1; j <= have[i]; j ++)
if(find(sta[i][j])) {
sta[i][have[i]] = x;
return 1;
}
}
}
return 0;
}
int main(int argc, const char * argv[]) {
while(scanf("%d %d", &n, &m) != EOF){
memset(have, 0, sizeof(have));
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
scanf("%d", &link[i][j]);
for (int i = 1; i <= m; i ++)
scanf("%d", &star[i]);
int i;
for (i = 1; i <= n; i ++) {
memset(used, 0, sizeof(used));
if(!find(i))
break;
}
if(i > n) printf("YES\n");
else printf("NO\n");
}
return 0;
}
如果按照一般的方法会超时,但是星球的数量≤10,这时候可以用状压来建边,因为只有10个星球,那么状态一共只有2^10(1024) 种,可以用源点S 跟状态建边,权值为状态的数量
状态与星球建边,权值是状态的数量,星球与汇点T建边,权值是星球的容量
状态1->2^10,星球2^10+1->2^1+10
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define maxn 8000005
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007
struct Edge{
int v, w, nxt;
}edge[maxn];
int n, m;
int S, T, head[200005], cur[200005], dis[200005], gap[200005], aug[200005], pre[200005], a[2500], tot;
void init(){
memset(head, -1, sizeof(head));
tot = 0 ;
}
void addEdge(int u, int v, int w) {
edge[tot].v = v;
edge[tot].w = w;
edge[tot].nxt = head[u];
head[u] = tot ++;
edge[tot].v = u;
edge[tot].w = 0;
edge[tot].nxt = head[v];
head[v] = tot ++;
}
int SAP(int n) {
int max_flow = 0, u = S, v;
int id, minDis;
aug[S] = INF;
pre[S] = -1;
memset(dis, 0, sizeof(dis));
memset(gap, 0, sizeof(gap));
gap[0] = n;
for (int i = 0; i <= n; i ++)
cur[i] = head[i];
while(dis[S] < n) {
int flag = 0;
if(u == T) {
max_flow += aug[T];
for (v = pre[T]; v + 1; v = pre[v]) {
id = cur[v];
edge[id].w -= aug[T];
edge[id ^ 1].w += aug[T];
aug[v] -= aug[T];
if(edge[id].w == 0)
u = v;
}
}
for (int i = cur[u]; i + 1; i = edge[i].nxt) {
v = edge[i].v;
if(edge[i].w > 0 && dis[u] == dis[v] + 1) {
flag = 1;
pre[v] = u;
cur[u] = i;
aug[v] = min(aug[u], edge[i].w);
u = v;
break;
}
}
if(!flag) {
if(--gap[dis[u]] == 0)
break;
minDis = n;
cur[u] = head[u];
for (int i = head[u]; i + 1; i = edge[i].nxt) {
v = edge[i].v;
if(edge[i].w > 0 && dis[v] < minDis) {
minDis = dis[v];
cur[u] = i;
}
}
dis[u] = minDis + 1;
gap[dis[u]] ++;
if(u != S)
u = pre[u];
}
}
return max_flow;
}
int main(int argc, const char * argv[]) {
while(scanf("%d %d", &n, &m) != EOF){
init();
S = 0; T = (1 << (m + 1)) + m + 1;
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i ++) {
int sta = 0;
for (int j = 1, v; j <= m; j ++) {
scanf("%d", &v);
if(v) sta |= (1 << j);
}
a[sta] ++;//每种状态的数量
}
for (int i = 2; i < (1 << (m + 1)); i ++) {
if(a[i]) {
addEdge(S, i, a[i]);//源点跟状态建边
for (int j = 1; j <= m; j ++)
if(i & (1 << j))//如果喜欢j星球
addEdge(i, j + (1 << (m + 1)), a[i]);//那么状态跟星球建边
}
}
for (int i = 1, v; i <= m; i ++) {
scanf("%d", &v);
addEdge((1 << (m + 1)) + i, T, v);//星球跟汇点T建边
}
int d = SAP(T + 1);
if(d == n) printf("YES\n");
else printf("NO\n");
}
return 0;
}