HDU4975(最大流是否只有唯一解)
题目链接:HDU4975
题目意思:
给出一个的表格,现在你需要往每个空格里面填数,你只能填(0-9)范围内的数,现在再告诉你每一行的和以及每一列的和,现在询问你是否存在某种填数方案,如果存在是否唯一。
题目思路:
这是一个非常经典的最大流模型,我们可以类似二分图那样,将行作为二分图的左部,列作为二分图的右部,然后创建源点和汇点,然后看看流入左部的流量之和,是不是等于右部流出的流量之和,如果相等,那么肯定存在填数方案,问题是怎么判断存在多解。
方法是判断跑完最大流的残余网络上是否存在正权环(点数大于2)。
下图为该题第三个样例的网络流图
下图为上图的残余网络,红色箭头标明的为正权环
我们可以这么理解这个结论,首先如果存在正权环,那么必然是由原图中的反向边(网络流概念),和正向边共同组成。那么说明环上的反向边的流量,可以通过正向边流向汇点。从而改变图中的残余网络流,那么最大流的方案也就不唯一了。
那么怎么判断环路?还要大于2,因为互为正反边的两条边形成的环,不合法的。所以形成的环点数必然大于2,我们可以通过tarjan把所有强联通分量跑出来,看一下是否有某个强联通分量点数大于2,若存在则存在正权环,否则不存在。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
const LL inf = 0x3f3f3f3f;
class dinic {
private:
struct node {
int v, rev;
LL cap;
};
vector<vector<node>> G;
vector<int> dis, cur, vis, hc;
int s, d;
int bfs() {
fill(dis.begin(), dis.end(), 0);
dis[s] = 1;
queue<int> q;
q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
for(auto& i : G[u]) {
if(!dis[i.v] && i.cap > 0) {
dis[i.v] = dis[u] + 1;
if(i.v == d) return 1;
q.push(i.v);
}
}
}
return 0;
}
LL dfs(int u, LL low) {
if(u == d) return low;
LL flow = 0;
for(int &i = cur[u]; i < (int)G[u].size(); i++) {
auto &j = G[u][i];
if(dis[j.v] == dis[u] + 1 && j.cap > 0) {
LL k = dfs(j.v, min(low, j.cap));
j.cap -= k;
G[j.v][j.rev].cap += k;
flow += k;
low -= k;
if(low == 0) break;
}
}
return flow;
}
public:
dinic(int n) {
G.resize(n);
dis.resize(n);
cur.resize(n);
}
void add(int u, int v, LL w) {
G[u].push_back({v, (int)G[v].size(), w});
G[v].push_back({u, (int)G[u].size()-1, 0});
}
LL maxflow(int s, int d) {
LL ans = 0;
this->s = s;
this->d = d;
while(bfs()) {
fill(cur.begin(), cur.end(), 0);
ans += dfs(s,inf);
}
return ans;
}
bool check(int n) {
vector<int> dfn(n), low(n), bl(n, -1);
int tot = 1;
int num = 0;
stack<int, vector<int>> s;
function<void(int)> dfs = [&](int u) {
dfn[u] = low[u] = tot++;
s.push(u);
for (auto i : G[u]) {
if(i.cap == 0) continue;
if (!dfn[i.v]) {
dfs(i.v);
low[u] = min(low[u], low[i.v]);
}
else if (bl[i.v] == -1)
low[u] = min(dfn[i.v], low[u]);
}
if (low[u] == dfn[u]) {
int tmp;
do {
tmp = s.top();
bl[tmp] = num;
s.pop();
} while (tmp != u);
num++;
}
};
for (int i = 0; i < n; i++) {
if (bl[i] == -1) {
tot = 1;
dfs(i);
}
}
vector<int>vis(n, 0);
for(int i = 0; i < n; i++){
if(bl[i] == -1) continue;
vis[bl[i]]++;
if(vis[bl[i]] > 2) return true;
}
return false;
}
};
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int t; cin >> t;
for(int ca = 1; ca <= t; ca++) {
int n, m; cin >> n >> m;
dinic a(n + m + 2);
int s = 0, d = n + m + 1;
LL sum1 = 0, sum2 = 0;
for(int i = 1; i <= n; i++) {
int tmp; cin >> tmp;
sum1 += tmp;
a.add(s, i, tmp);
for(int j = 1; j <= m; j++) a.add(i, j + n, 9);
}
for(int i = 1; i <= m; i++) {
int tmp; cin >> tmp;
sum2 += tmp;
a.add(i + n, d, tmp);
}
cout << "Case #" << ca << ": ";
if(sum1 == sum2) {
LL ans = a.maxflow(s, d);
if(ans == sum1) {
if(a.check(n + m + 2)) cout << "So young!" << endl;
else cout << "So simple!" << endl;
}
else cout << "So naive!" << endl;
}
else cout << "So naive!" << endl;
}
}