0-1分數規劃 網路流 最小割 最大流最小割 zoj2676 network
阿新 • • 發佈:2019-02-03
【題目大意】
選取一邊集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; }