裸最大密度子图:参考博客:
点击打开链接,论文:胡伯涛,最小割模型在信息学竞赛中的应用
/**
给定一个无向图,要求它的一个子图,使得子图中边数|E|与点数|V|的比值最大。
解法一:
假设答案为k ,则要求解的问题是:选出一个合适的点集V和边集E,令(|E| - k * |V|) 取得最大值。所谓“合适”是指满足如下限制:若选择某条边,则必选择其两端点。
建图:以原图的边作为左侧顶点,权值为1;原图的点作为右侧顶点,权值为-k(相当于 支出k)。
若原图中存在边(u,v),则新图中添加两条边([uv]-->u), ([uv]-->v),转换为最大权闭合子图。
解法二:
把原图中的无向边转换成两条有向边,容量为1。
设一源点,连接所有点,容量为U(取m)。
设一汇点,所有点连接汇点,容量为 U+2g-dv 。
二分枚举最大密度g,其中dv为v的度。
判断(U*n-MaxFlow)/2.>=0。
最后跳出的L就是最大密度。
拿这个L再重新建图,求最大流。
然后从s出发bfs或者dfs,走残留容量大于0的边,所有能到达的点就是答案。
具体分析过程见胡伯涛的论文《最小割模型在信息学竞赛中的应用》
**/
/**
nowcode 第十四届华中科技大学程序设计竞赛决赛同步赛 K
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int inf = 1e9;
const int maxn = 4000;
const double eps = 1e-8;
typedef long long ll;
struct Edge
{
int fr,to;
double cap,flow;
};
struct Dinic
{
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn+5];
bool vis[maxn+5];
int d[maxn+5];
int cur[maxn+5];
void Init(int n)
{
this->n = n;
for(int i=0; i<=n; i++) G[i].clear();
edges.clear();
}
void Addedge(int fr,int to,double cap)
{
edges.push_back((Edge)
{
fr,to,cap,0
});
edges.push_back((Edge)
{
to,fr,0,0
});
m = edges.size();
G[fr].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s] = 0, vis[s] = 1;
while(!Q.empty())
{
int x = Q.front();
Q.pop();
for(int i=0,l=G[x].size(); i<l; i++)
{
Edge &e = edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to] = 1;
d[e.to] = d[x] + 1;
Q.push(e.to);
}
}
}
return vis[t];
}
double DFS(int x,double a)
{
if(x==t||a==0) return a;
double flow = 0,f;
for(int &i=cur[x],l=G[x].size(); i<l ; i++)
{
Edge &e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
{
e.flow+=f;
edges[G[x][i]^1].flow -=f;
flow+=f;
a-=f;
if(a==0) break;
}
}
return flow;
}
double Maxflow(int s,int t)
{
this->s = s, this->t = t;
double flow = 0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,(double)inf);
}
//cout<<flow<<endl;
return flow;
}
}my;
int n,a[105],x[10005],y[10005],tot,dv[105];
bool check2(double k)
{
my.Init(n+2);
for(int i=1;i<=tot;i++)
{
my.Addedge(x[i],y[i],1);
my.Addedge(y[i],x[i],1);
}
for(int i=1;i<=n;i++)
{
my.Addedge(0,i,tot);
my.Addedge(i,n+1,tot+2*k-dv[i]);
}
return ((tot*n - my.Maxflow(0,n+1))/2 >= eps );
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
if(a[i]%a[j]==0||a[j]%a[i]==0) x[++tot] = i, y[tot] = j, dv[i]++,dv[j]++;
}
double l = 0,r = n, mid;
for(int i=1;i<=30;i++)
{
mid = (l+r)/2;
if(check2(mid)) l = mid;
else r = mid;
}
printf("%.10f\n",l);
return 0;
}