题意: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;
}