pku 3155 Hard Life 最大密集子图
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; }