您的位置:首页 > 其它

【最小割模型、01分数规划】zoj2676Network Wars

2015-08-05 21:33 369 查看
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2676

题目描述:对给定的无向图,n(2<=n<=100)(2<=n<=100)个点,m(1<=m<=400)(1<=m<=400)条边,源点为1,汇点为n。求一个割,使得该割边集的平均边权最小。

这道题在Amber大神的论文《最小割模型在信息学竞赛中的应用》中提出。建议大家去看一看。在13页。

论文地址

令xix_i表示第ii条边是否在割集中。当然权值为负的边直接放入割集中。

对于01分数规划问题,先构造一个新函数g(a)=min((w−ac)∗x)g(a)=min((w-ac)*x)其中w表示边的权值之和,c表示边的条数。

该函数是单调递减的,而最优解是当g(a)=0g(a)=0时得到。

如何check(mid)?

建图。

做一次最小割,判断割的容量为正还是为负。

特别注意,该题有浮点数运算,会存在浮点误差。(我就是在这里被坑了几次 %>_<%)

具体操作见代码。

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 405
#define MAXM 8005
#define eps 1e-4
using namespace std;

int n ,m ,vd[MAXN] ,d[MAXN] ,s ,t ,uplimt ,a[MAXN] ,b[MAXN] ,temp[MAXN] ;
bool vis[MAXN] ;
double c[MAXN] ;

struct node
{
int v ;
double w ;
node *next ,*back ;
}edge[MAXM] ,*adj[MAXN] ,*code=edge ;

void add(int a,int b,double c)
{
node *p=++code;
p->v=b ,p->next=adj[a] ,p->w=c ,p->back=code+1 ;
adj[a]=p ;
p=++code ;
p->v=a ,p->next=adj[b] ,p->w=0 ,p->back=code-1 ;
adj[b]=p ;
}

double aug(int u,double augco)
{
if(u==t)
return augco;
int mind=uplimt ,v ;
double augc=augco ,delta ;
for(node *p=adj[u];p!=NULL;p=p->next)
if(p->w >0)
{
v=p->v;
if(d[u]==d[v]+1)
{
delta=min(augc,p->w);
delta=aug(v,delta);
augc-=delta ,p->w-=delta ,p->back->w+=delta ;
if(d[s]>=uplimt)return augco-augc;
if(augc<eps)break; //注意:不能写成if(!augc)break;
}
mind=min(mind,d[v]);
}
if(augco==augc)
{
--vd[d[u]];
if(vd[d[u]]==0)
d[s]=uplimt ;
d[u]=mind+1;
++vd[d[u]];
}
return augco-augc;
}

double sap()
{
memset(d,0,sizeof d);
memset(vd,0,sizeof vd);
double flow=0;
vd[0]=uplimt;

while(d[s]<uplimt)
flow+=aug(s,2147483647);
return flow ;
}

bool check(double mid)
{
memset(adj,0,sizeof adj);
code=edge ;
s=1 ,t=n ,uplimt=t+1 ;

double ans=0 ,tmp ;
for(int i=0;i<m;++i)
{
tmp=c[i]-mid ;
if(tmp>0)
{
add(a[i],b[i],tmp);
add(b[i],a[i],tmp);
}
else ans+=tmp ;
}
ans+=sap();
return ans>=0;
}

void dfs(int u)
{
vis[u]=1;
for(node *p=adj[u];p!=NULL;p=p->next)
if(p->w>eps&&!vis[p->v])
dfs(p->v);
}

void work()
{
for(int i=0;i<m;++i)
scanf("%d%d%lf",&a[i],&b[i],&c[i]);
double mid ,ans=0 ,l=0.0 ,r=10000010.0 ;
while(r-l>eps)
{
mid=(r+l)/2.0 ;
if(check(mid))
ans=l=mid;
else r=mid;
}

memset(vis,0,sizeof vis);
dfs(s);
double tmp ;
int cnt=0 ;
for(int i=0;i<m;++i)
{
tmp=c[i]-ans;
if(tmp<0)
temp[++cnt]=i+1;
else if(vis[a[i]]^vis[b[i]])
temp[++cnt]=i+1;
}
printf("%d\n",cnt);
for(int i=1;i<cnt;++i)
printf("%d ",temp[i]);
printf("%d\n",temp[cnt]);
}

int main()
{
while(~scanf("%d%d",&n,&m))
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: