【题目大意】
选取一边集E,使得边集的平均值最小
注意边集中必须包含割(题目要求)
于是 分数规划,二分. 对于二分的每一个 mid , 将 weight <= mid的 添加入临时解集,并对于剩下的网络(weight -= mid)求一个最小割,然后所求的集合便是mid 条件下的边集
最后输出边集既可
一个无向图,求最小的平均割:,其中
,wi表示边权,该表达式的意思就是选择某些边集构成S-T割,使得平均割最小。
分析:0-1分数规划问题,设,则令mincut=
因为ci只能取0或1所以转换模型,边权值变为
,每条边取与不取使得取到的边集成为一个最小S-T割,因为这个0-1分数规划满足单调性且当且仅当mincut=0时
取到最小值,mincut<0时
小于最优解,mincut>0时
大于最优解,所以可以二分
的值,使得min=0。这里对于
的边一定是可取的,因为这些边只会减小最小割所以一定是可取的。最后二分的时候注意精度。
可以参见胡伯涛的论文
学0-1规划慢慢细细的学了2天真是太失败了。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<memory.h>
#include<cmath>
using namespace std;
#define MAXN 500
#define MAXE 40000
#define INF 0x7fffffff
#define EPS 1e-6
int mark[MAXN];
int x[MAXN],y[MAXN];
double c[MAXN];
int n,m;
int nv,ne;
int s,t,idx;
struct Edge{
long next,pair;
long v;
double cap,flow;
}edge[MAXE];
long net[MAXN];
double sap()
{
long numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN];
long u,tmp,neck,i;
double cur_flow,max_flow;
memset(dist,0,sizeof(dist));
memset(numb,0,sizeof(numb));
memset(pre,-1,sizeof(pre));
for(i = 1 ; i <= nv ; ++i)
curedge[i] = net[i];
numb[nv] = nv;
max_flow = 0;
u = s;
while(dist[s] < nv)
{
/* first , check if has augmemt flow */
if(u == t)
{
cur_flow = INF;
for(i = s; i != t;i = edge[curedge[i]].v)
{
if(cur_flow > edge[curedge[i]].cap+EPS )
{
neck = i;
cur_flow = edge[curedge[i]].cap;
}
}
for(i = s; i != t; i = edge[curedge[i]].v)
{
tmp = curedge[i];
edge[tmp].cap -= cur_flow;
edge[tmp].flow += cur_flow;
tmp = edge[tmp].pair;
edge[tmp].cap += cur_flow;
edge[tmp].flow -= cur_flow;
}
max_flow += cur_flow;
u = neck;
}
/* if .... else ... */
for(i = curedge[u]; i != -1; i = edge[i].next)
if(edge[i].cap > EPS && dist[u] == dist[edge[i].v]+1)
break;
if(i != -1)
{
curedge[u] = i;
pre[edge[i].v] = u;
u = edge[i].v;
}else{
if(0 == --numb[dist[u]]) break;
curedge[u] = net[u];
for(tmp = nv,i = net[u]; i != -1; i = edge[i].next)
if(edge[i].cap > EPS)
tmp = tmp<dist[edge[i].v]?tmp:dist[edge[i].v];
dist[u] = tmp + 1;
++numb[dist[u]];
if(u != s) u = pre[u];
}
}
return max_flow;
}
void add(int u,int v,double cl)
{
idx++;
edge[idx].v=v;
edge[idx].cap=cl;
edge[idx].flow=0;
edge[idx].next=net[u];
edge[idx].pair=idx+1;
net[u]=idx++;
edge[idx].next=net[v];
edge[idx].v=u;
edge[idx].cap=cl;
edge[idx].flow=0;
edge[idx].pair=idx-1;
net[v]=idx++;
}
void init()
{
memset(net,-1,sizeof(net));
idx=0;
s=1;
t=n;
}
double binary()
{
double low,hig,mid,flow;
int i;
low=0;hig=10000001;
while(low+EPS<hig)//二分搜索
{
mid=(low+hig)/2;
flow=0;
init();
for(i=1;i<=m;i++)
{
if(c[i]+EPS<mid)
{
flow+=c[i]-mid;
}
else
{
add(x[i],y[i],c[i]-mid);
}
}
flow+=sap();
if(flow>EPS)
{
low=mid;
}
else
{
hig=mid;
}
}
return mid;
}
void dfs(int u)
{
for(int i=net[u];i!=-1;i=edge[i].next)//啊啊啊啊啊啊啊
{
if(edge[i].cap>EPS && !mark[edge[i].v])
{
mark[edge[i].v]=1;
dfs(edge[i].v);
}
}
}
void solve()
{
int i,first;
double rat=binary();//二分得到最小比率值
memset(mark,0,sizeof(mark));
mark[s]=1;
dfs(s);//深搜找割
int ans=0;
/*后面是输出答案,用mark数组标记,如果c[i]在二分最小比率值一下
且mark两个点中存在一个值为1则统计 */
for(i=1;i<=m;i++)
{
if(c[i]+EPS < rat || (mark[x[i]]+mark[y[i]])==1)
{
ans++;
}
}
printf("%d\n",ans);
first=1;
for(i=1;i<=m;i++)
{
if(c[i]+EPS < rat || (mark[x[i]]+mark[y[i]])==1)
{
if(first)
{
printf("%d",i);
first=0;
}
else
printf(" %d",i);
}
}
puts("");
}
int main()
{
int i;
int first=1;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!first)puts("");
first=0;
for(i=1;i<=m;i++)
{
scanf("%d%d%lf",&x[i],&y[i],&c[i]);
}
ne=m;
nv=n;
solve();
}
return 0;
}