题意:世界末日,地球上有n个人,现有适合人类居住的有m个星球,每个人都有自己想要去的星球,问在满足每个人的需求的情况下能不能把所有的人全部救走。
算法:
TLE思路一:
根据题意和直觉来讲,确实是最大流,超级源点到每个人连线,再从每个人到他想要的星球连线,然后这样下来最复杂就会有1e6条边,dinic跑下来肯定会T,然后就去搜了题解有了第二种解法
TLE代码:
#include <bits/stdc++.h>
using namespace std;
#define met(a, b) memset(a, b, sizeof(a))
const int inf = 0x7fffffff;
const int maxn = 1000000 + 5;
struct EDGE {
int to, vol, next;
} edges[1000000];
int n, m, allPeople[50], edgesNum = 0, Head[maxn];
int dep[maxn], cur[maxn];
int S, T;
void buildGraph();
void addEdge(int u, int v, int w);
bool bfs(int s, int t);
int dfs(int u, int flow);
int dinic(int s, int t);
int main() {
while(~scanf("%d%d", &n ,&m)) {
S = 0;
T = n + m + 1;
edgesNum = 0;
met(Head, -1);
int d;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
scanf("%d", &d);
if(d == 1) addEdge(i, n + j, 1);
}
}
for(int i = 1; i <= m; i++) {
scanf("%d", &allPeople[i]);
}
buildGraph();
int ans = dinic(S, T);
if(ans == n) puts("YES");
else puts("NO");
}
return 0;
}
void buildGraph() {
for(int i = 1; i <= n; i++) {
addEdge(S, i, 1);
}
for(int i = 1; i <= m; i++) {
addEdge(n + i, T, allPeople[i]);
}
}
void addEdge(int u, int v, int w) {
edges[edgesNum].to = v;
edges[edgesNum].vol = w;
edges[edgesNum].next = Head[u];
Head[u] = edgesNum++;
edges[edgesNum].to = u;
edges[edgesNum].vol = 0;
edges[edgesNum].next = Head[v];
Head[v] = edgesNum++;
}
bool bfs(int s, int t) {
met(dep, -1);
dep[s] = 1;
queue<int> q;
q.push(s);
while(!q.empty()) {
int u = q.front();
if(u == t) return 1;
q.pop();
for(int e = Head[u]; e != -1; e = edges[e].next) {
int v = edges[e].to;
if(dep[v] == -1 && edges[e].vol > 0 && v != s) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
if(dep[t] == -1) return 0;
return 0;
}
int dfs(int u, int flow) {
if(u == T) return flow;
for(int &e = cur[u]; ~e; e = edges[e].next) {
int v = edges[e].to;
if(dep[v] == dep[u] + 1 && edges[e].vol > 0) {
int di = dfs(v, min(flow, edges[e].vol));
if(di > 0) {
edges[e].vol -= di;
edges[e ^ 1].vol += di;
return di;
}
}
}
return 0;
}
int dinic(int s, int t) {
int max_flow = 0;
while(bfs(s, t)) {
for(int i = S; i <= T; i++) cur[i] = Head[i];
while(int di = dfs(s, inf)) {
max_flow += di;
}
}
return max_flow;
}
AC思路二:
分析题目的数据量来看,有1e5个人,但是星球的个数为10个,那么分析来看最多有2^10=1024种不同的选择,又因为1e5 > 1024所以有很多人的选择是完全一模一样的,然后我们可以把这些人归为一类人,具体方法就是状态压缩,运用位运算把每个人压缩一下,然后选择完全一样的人就会被压缩到一起,这样做的话在建图的时候需要修改超级源点到人的容量以及人到星球的容量,压缩之后边的数目就会最复杂成为10240条,dinic就是可行的方案了。
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define met(a, b) memset(a, b, sizeof(a))
const int inf = 0x7fffffff;
const int maxn = 1000000 + 5;
struct EDGE {
int to, vol, next;
} edges[1000000];
int n, m, allPeople[maxn], edgesNum = 0, Head[maxn];
int dep[maxn], cur[maxn];
map<int, int> cnt;
int S, T, tot;
void buildGraph();
void addEdge(int u, int v, int w);
bool bfs(int s, int t);
int dfs(int u, int flow);
int dinic(int s, int t);
int main() {
while(~scanf("%d%d", &n ,&m)) {
S = 0;
tot = 0;
T = n + m + 1;
cnt.clear();
edgesNum = 0;
met(Head, -1);
int d, sum = 0;
for(int i = 1; i <= n; i++) {
int res = 0;
for(int j = 1; j <= m; j++) {
scanf("%d", &d);
if(d == 1) res += (d << j);
}
cnt[res]++;
}
T = cnt.size() + m + 1;
for(int i = 1; i <= m; i++) {
scanf("%d", &allPeople[i]);
sum += allPeople[i];
}
if(sum < n) {
puts("NO");
continue;
}
buildGraph();
int ans = dinic(S, T);
if(ans >= n) puts("YES");
else puts("NO");
}
return 0;
}
void buildGraph() {
map<int, int>::iterator it;
for(it = cnt.begin(); it != cnt.end(); it++) {
tot++;
addEdge(S, tot, it -> second);
for(int i = 1; i <= m; i++) {
if((it -> first) & (1 << i)) {
addEdge(tot, cnt.size() + i, it -> second);
}
}
}
for(int i = 1; i <= m; i++) {
addEdge(tot + i, T, allPeople[i]);
}
}
void addEdge(int u, int v, int w) {
edges[edgesNum].to = v;
edges[edgesNum].vol = w;
edges[edgesNum].next = Head[u];
Head[u] = edgesNum++;
edges[edgesNum].to = u;
edges[edgesNum].vol = 0;
edges[edgesNum].next = Head[v];
Head[v] = edgesNum++;
}
bool bfs(int s, int t) {
met(dep, -1);
dep[s] = 1;
queue<int> q;
q.push(s);
while(!q.empty()) {
int u = q.front();
if(u == t) return 1;
q.pop();
for(int e = Head[u]; e != -1; e = edges[e].next) {
int v = edges[e].to;
if(dep[v] == -1 && edges[e].vol > 0 && v != s) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
if(dep[t] == -1) return 0;
return 0;
}
int dfs(int u, int flow) {
if(u == T) return flow;
for(int &e = cur[u]; ~e; e = edges[e].next) {
int v = edges[e].to;
if(dep[v] == dep[u] + 1 && edges[e].vol > 0) {
int di = dfs(v, min(flow, edges[e].vol));
if(di > 0) {
edges[e].vol -= di;
edges[e ^ 1].vol += di;
return di;
}
}
}
return 0;
}
int dinic(int s, int t) {
int max_flow = 0;
while(bfs(s, t)) {
for(int i = S; i <= T; i++) cur[i] = Head[i];
while(int di = dfs(s, inf)) {
max_flow += di;
}
}
return max_flow;
}