http://poj.org/problem?id=3155
题意:
给定一个无向图,求该图的一个子图使得该子图中边/点的权值最大;
思路:
证明看论文:http://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html
建图方法:
把原图中的无向边转换成两条有向边,容量为1;
设一源点,连接所有点,容量为U
设一汇点,所有点连接汇点,容量为 U+2g-du
二分枚举最大密度g,其中du为u的度。
U一般取边数即可。
//#pragma comment(linker,"/STACK:327680000,327680000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>
#define CL(arr, val) memset(arr, val, sizeof(arr))
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x) (x) << 1
#define R(x) (x) << 1 | 1
#define MID(l, r) (l + r) >> 1
#define Min(x, y) (x) < (y) ? (x) : (y)
#define Max(x, y) (x) < (y) ? (y) : (x)
#define E(x) (1 << (x))
#define iabs(x) (x) < 0 ? -(x) : (x)
#define OUT(x) printf("%I64d\n", x)
#define lowbit(x) (x)&(-x)
#define Read() freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);
#define M 1000007
#define N 1107
using namespace std;
const double inf = ~0u;
const double eps = 1e-6;
double lim;
struct node
{
int v;
double w;
int next;
}g[M];
int head[N],ct;
int q[N*100];
int level[N];
bool vt[N];
struct t_node
{
int u,v;
}t_g[N];
int path[N],len;
int du[N];
int n,m;
void add(int u,int v,double w)
{
g[ct].v = v;
g[ct].w = w;
g[ct].next = head[u];
head[u] = ct++;
g[ct].v = u;
g[ct].w = 0;
g[ct].next = head[v];
head[v] = ct++;
}
bool layer(int s,int e){
int i;
CL(level,-1);
level[s] = 0;
int l,r;
l = r = 0;
q[r] = s;
while (l <= r){
int u = q[l++];
for (i = head[u]; i != -1; i = g[i].next){
int v = g[i].v;
if (level[v] == -1 && g[i].w > eps){
level[v] = level[u] + 1;
q[++r] = v;
if (v == e) return true;
}
}
}
return false;
}
double find(int s,int e){
int i;
double ans = 0;
int top = 1;
while (top){
int u = (top == 1 ? s : g[q[top - 1]].v);//如果没有变肯定是起点,否则就是上一个边终点
if (u == e){
double MIN = inf;
int pos;
//找出最小流量
for (i = 1; i < top; ++i){
int tp = q[i];//注意这里取边的编号
if (g[tp].w < MIN){
MIN = g[tp].w;
pos = i;
}
}
//更新容量
for (i = 1; i < top; ++i){
int tp = q[i];//注意这里取边的编号
g[tp].w -= MIN;
g[tp^1].w += MIN;
}
ans += MIN;
top = pos;
}
else{//找可行流
for (i = head[u]; i != -1; i = g[i].next){
int v = g[i].v;
if (g[i].w > eps && level[v] == level[u] + 1){
q[top++] = i;
break;
}
}
if (i == -1){//如果u没有可走的子节点
top--;
level[u] = -1;
}
}
}
return ans;
}
double dinic(int s,int e,double mid)
{
int i;
//建图是关键
CL(head,-1); ct = 0;
for (i = 1; i <= m; ++i)
{
int u = t_g[i].u;
int v = t_g[i].v;
add(u,v,1.0); add(v,u,1.0);
}
for (i = 1; i <= n; ++i)
{
add(s,i,m);
add(i,e,1.0*m + 2*mid - du[i]);
}
//求最小割
double ans = 0;
while (layer(s,e)) ans += find(s,e);
return ans;
}
void dfs(int s)
{
vt[s] = true;
for (int i = head[s]; i != -1; i = g[i].next)
{
int v = g[i].v;
double w = g[i].w;
if (!vt[v] && w > eps)
{
path[len++] = v;
dfs(v);
}
}
}
int main()
{
//Read();
int i;
while (~scanf("%d%d",&n,&m))
{
if (m == 0)
{
printf("1\n1\n");
continue;
}
CL(du,0);
for (i = 1; i <= m; ++i)
{
scanf("%d%d",&t_g[i].u,&t_g[i].v);
du[t_g[i].u]++;
du[t_g[i].v]++;
}
double l = 1.0/n;
double r = m*1.0;
double mid = 0;
int s = 0;
int e = n + 1;
lim = 1.0/(n*n);
while (r - l > lim)//证明有:任意两个最大密度子图的差值不会超过1/(n*n)
{
mid = (l + r)/2;
double tmp = dinic(s,e,mid);
double ans = (m*n - tmp)/2.0;//得到值
if (ans > eps) l = mid;
else r = mid;
}
dinic(s,e,l);
CL(vt,false);
len = 0;
dfs(s);
sort(path,path + len);
printf("%d\n",len);
for (i = 0; i < len; ++i) printf("%d\n",path[i]);
}
return 0;
}