UVA1389 Hard Life
求出原图的一个子图 ,最大化 。
,时限 。
std
最大密度子图。
按照套路,考虑二分 。
则有 。
即可看作,在原图上找一个最大权闭合子图,其所有边的权为 ,所有点的权为 。
但是这个涉及到了边权,不太好搞。
想到由于选了一条边,就相当于选了这条边对应的两个点。
那么把边看成一个点,上面那个问题可以转化为如下模型(根据最大权闭合子图的性质):
- 向每一条原图的边(把边看作点)连流量为 的边。
- 每一条原图的边向其对应的原图上的两个点连流量为 的边(其可看作选了原图的一条边,就得选其对应的两个点)。
- 每一个原图的上点向 连流量为 的边。
那么上述模型见图跑网络流即可。
记最大流为 ,那么最后求出的最大权闭合子图的权值为 。
判断 是否 ,然后继续二分即可。
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
inline void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
typedef double tp;
const int _ = 2e4 + 10;
const double eps = 1e-7, inf = 1e7;
int n, m, s, t, lv[_], cur[_];
int tot = 1, head[_], to[_ << 1], nxt[_ << 1];
tp w[_ << 1];
inline void add(int u, int v, tp dis)
{
to[++tot] = v;
nxt[tot] = head[u];
w[tot] = dis;
head[u] = tot;
}
inline void Add(int u, int v, tp dis)
{
add(u, v, dis);
add(v, u, 0);
}
inline bool bfs()
{
memset(lv, 0, sizeof lv);
lv[s] = 1;
memcpy(cur, head, sizeof(head));
queue<int> q;
q.push(s);
while (!q.empty())
{
int p = q.front();
q.pop();
for (int eg = head[p]; eg; eg = nxt[eg])
{
int v = to[eg];
tp vol = w[eg];
if (vol > eps && lv[v] == 0)
{
lv[v] = lv[p] + 1, q.push(v);
if(v == t) return 1;
}
}
}
return 0;
}
tp dfs(int p = s, tp flow = inf)
{
if (p == t)
return flow;
tp rmn = flow;
for (int eg = cur[p]; eg && rmn > eps; eg = nxt[eg])
{
cur[p] = eg;
int v = to[eg];
tp vol = w[eg];
if (vol > eps && lv[v] == lv[p] + 1)
{
tp c = dfs(v, min(vol, rmn));
if(c < eps) lv[v] = 0;
rmn -= c;
w[eg] -= c;
w[eg ^ 1] += c;
}
}
return flow - rmn;
}
inline tp dinic()
{
tp ans = 0;
while (bfs())
ans += dfs();
return ans;
}
int Ans, fr[_], too[_];
bool check(tp mid)
{
tot = 1;
memset(head, 0, sizeof head);
for (int i = 1; i <= m; ++i)
Add(s, i + n, 1);
for (int i = 1; i <= n; ++i)
Add(i, t, mid);
for (int i = 1; i <= m; ++i)
{
Add(i + n, fr[i], inf);
Add(i + n, too[i], inf);
}
return (tp)(m * 1.0 - dinic()) > eps;
}
bool vis[_];
void FA(int u)
{
vis[u] = 1;
for (int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if (!vis[v] && w[i])
FA(v);
}
}
signed main()
{
int x, y;
s = 0, t = _ - 1;
while (~scanf("%d%d", &n, &m))
{
Ans = 0;
if (!m)
{
printf("%d\n%d\n\n", 1, 1);
continue;
}
for (int i = 1; i <= m; ++i)
fr[i] = read(), too[i] = read();
tp l = 0, r = m, mid;
while (r - l > eps)
{
mid = (l + r) / 2.0;
if (check(mid))
l = mid;
else
r = mid;
}
check(l);
memset(vis, 0, sizeof vis);
FA(s);
for (int i = 1; i <= n; ++i)
if (vis[i])
Ans++;
write(Ans), putchar('\n');
for (int i = 1; i <= n; ++i)
if (vis[i])
write(i), putchar('\n');
putchar('\n');
}
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122030