ZOJ 2676 Network Wars 0-1分数规划,最小割

题意:n点m条带权边的无向图.起点为s终点为t.

n<=100.m<=400.输出一个边集S 使得删掉S后,s-t不存在路径. 并且 Σ(w[S[i]]) / |S| 最小

 

要求最小化  λ  = f(x) = Σ w[i] * x[i]   / Σ c[i] *x[i] =  w*x /  c*x       [ x[i]代表选第i条边. 并且向量x要构成一个边割集.]

令 g(λ) = min { (w-cλ) *x   }   

性质:g(λ)是单调递减.  并且当g(λ)=0时 λ为原问题的最优解.

现在二分λ求g(λ), 找到一个解向量x使g(λ)最小.

解向量x 要构成边割集并且代价最小. 

现在取第i条边的代价为 w[i]- cλ.把第i条边容量赋值为w[i]-cλ,求一次最小割即可.

 

求最小割的边集: 若在残余网络中s-u存在一条路径 则u属于S.剩余的点都属于T. 横跨S-T的边加入边集即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> ii;
const int N=4e2+5,M=2e5+5;
const ld eps=1e-10,inf=2e15;
int n,m,tot,s,t,head[N],dis[N];
int a[N],b[N],vis[N];
ld c[N];
struct edge{
	int to,nxt;
	ld cap;
	edge(){}
	edge(int to,int nxt,ld cap):to(to),nxt(nxt),cap(cap){}
}e[M];
void add_edge(int u,int v,ld cap){
	e[tot]=edge(v,head[u],cap);
	head[u]=tot++;
	e[tot]=edge(u,head[v],0);
	head[v]=tot++;
}
void init(){
	s=1,t=n;
	tot=0;
	memset(head,-1,sizeof(head));
}
bool bfs(){
	queue<int> q;
	memset(dis,-1,sizeof(dis));
	dis[s]=0,q.push(s);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=e[i].nxt){
			int v=e[i].to;
			if(dis[v]==-1&&e[i].cap>0)	dis[v]=dis[u]+1,q.push(v);
		}
	}
	return dis[t]!=-1;
}
ld dfs(int u,ld x){
	if(u==t||x==0)	return x;
	ld res=0;
	for(int i=head[u];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if(dis[v]!=dis[u]+1)	continue;
		ld dx=dfs(v,min(x,e[i].cap));
		if(dx>0){//go back
			e[i].cap-=dx;
			e[i^1].cap+=dx;
			x-=dx,res+=dx;
			if(x==0)	return res;
		}
	}
	dis[u]=-1;
	return res;
}
ld Dinic(){
	ld res=0;
	while(bfs())res+=dfs(s,inf);
	return res;
}
bool check(ld x){
	init();
	ld res=0;
	for(int i=1;i<=m;i++){
		if(c[i]-x<=0)	res=res+c[i]-x;
		else add_edge(a[i],b[i],c[i]-x),add_edge(b[i],a[i],c[i]-x);
	}
	res+=Dinic();
    return res>=0;
}
void dfs(int u){
	vis[u]=true;
	for(int i=head[u];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if(e[i].cap>0&&!vis[v])		dfs(v);
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	while(cin>>n>>m){
		init();
		ld l=0,r=0,lam;
		for(int i=1;i<=m;i++)	cin>>a[i]>>b[i]>>c[i],r+=c[i];
		while(r-l>eps){
			ld mid=(l+r)/2.0;
			if(check(mid))//g(λ)>=0
				l=mid,lam=mid;
			else	 
				r=mid;
		}
	//	cout<<fixed<<setprecision(10)<<lam<<' ';	cout<<fixed<<setprecision(10)<<sum<<'\n';
		memset(vis,0,sizeof(vis));
		dfs(s);
		vector<int> res;
		for(int i=1;i<=m;i++)	if(vis[a[i]]+vis[b[i]]==1||c[i]-lam<=0)	res.push_back(i);
		int sz=int(res.size());
		printf("%d\n",sz);
		for(int i=0;i<sz-1;i++)	printf("%d ",res[i]);
		printf("%d\n",res[sz-1]);
	}
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值