题意
传送门 POJ 3155
题解
算法思路
参考《最小割模型在信息学竞赛中的应用》。分数规划,求满足条件的 最大值
为子图边数, 为子图点数,不等式左边为图的密度。不同子图 密度差 不小于 ,考虑到多边形点与边的关系,图密度的 二分范围 为 。为了对应最小割模型,上式取负后问题转化为
这里求的是边数,边权为 。对于与点相连的边集,采用求 补集 的思想
为点的 度,即作为端点的边的权求和(这里是边的数量), 为 的补集。此时
对于
假设 为包含源点的点集 ,考虑到可能出现 负权值 的边,边权值要加上一个足够大的值 ,则每一个顶点向汇点 连一条容量为 的边;对应地,源点 向每一个顶点连一条容量为 的边。对于
则对于无向图的每一条边的端点,连两条容量为 的有向边。设最大密度为
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-10
#define PI 3.14159265358979323846
using namespace std;
typedef double cap_type;
#define MAX_V 105
struct edge{
int to, rev;
cap_type cap;
edge(int to, cap_type cap, int rev) : to(to), cap(cap), rev(rev){}
};
vector<edge> G[MAX_V];
int level[MAX_V], iter[MAX_V];
void add_edge(int from, int to, cap_type cap){
G[from].push_back(edge(to, cap, G[to].size()));
G[to].push_back(edge(from, 0, G[from].size() - 1));
}
void bfs(int s){
memset(level, -1, sizeof(level));
queue<int> que;
level[s] = 0;
que.push(s);
while(!que.empty()){
int v = que.front(); que.pop();
for(int i = 0; i < G[v].size(); i++){
edge &e = G[v][i];
if(e.cap > eps && level[e.to] < 0){
level[e.to] = level[v] + 1;
que.push(e.to);
}
}
}
}
cap_type dfs(int v, int t, cap_type f){
if(v == t) return f;
for(int &i = iter[v]; i < G[v].size(); i++){
edge &e = G[v][i];
if(e.cap > eps && level[v] < level[e.to]){
cap_type d = dfs(e.to, t, min(f, e.cap));
if(d > eps){
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
cap_type max_flow(int s, int t){
cap_type flow = 0;
for(;;){
bfs(s);
if(level[t] < 0) return flow;
memset(iter, 0, sizeof(iter));
cap_type f;
while((f = dfs(s, t, INF)) > eps){
flow += f;
}
}
}
#define MAX_N 105
#define MAX_M 1005
int N, M;
int a[MAX_M], b[MAX_M];
double d[MAX_N];
int cnt;
bool used[MAX_N];
int res[MAX_N];
void dfs(int u){
used[u] = 1;
if(u < N) res[cnt++] = u + 1;
for(int i = 0; i < G[u].size(); i++){
edge &e = G[u][i];
if(e.cap > eps && !used[e.to]) dfs(e.to);
}
}
bool C(double x){
// 最大密度子图建图
int s = N, t = s + 1, V = t + 1;
double inf = M;
for(int v = 0; v < V; v++) G[v].clear();
for(int i = 0; i < N; i++){
add_edge(s, i, inf);
add_edge(i, t, inf + 2 * x - d[i]);
}
for(int i = 0; i < M; i++){
add_edge(a[i], b[i], 1);
add_edge(b[i], a[i], 1);
}
return inf * N - max_flow(s, t) > eps;
}
void solve(){
// 特判无边情况
if(M == 0){
if(N > 0) printf("1\n1\n");
return;
}
memset(d, 0, sizeof(d));
// 计算点的度
for(int i = 0; i < M; i++) ++d[a[i]], ++d[b[i]];
double lb = 1 / 2, ub = (N - 1) / 2, ep = 1.0 / N / N;
while(ub - lb > ep){
double mid = (ub + lb) / 2;
if(C(mid)) lb = mid;
else ub = mid;
}
memset(used, 0, sizeof(used));
// 求子图节点
C(lb);
cnt = 0;
dfs(N);
sort(res, res + cnt);
printf("%d\n", cnt);
for(int i = 0; i < cnt; i++) printf("%d\n", res[i]);
}
int main(){
while(~scanf("%d%d", &N, &M)){
for(int i = 0; i < M; i++){
scanf("%d%d", a + i, b + i);
--a[i], --b[i];
}
solve();
}
return 0;
}