本文主要是介绍zoj2676--Network Wars(0-1分数规划+最小割),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
zoj2676:题目链接
题目大意:有一个n个点的网络,其中有m条光缆(所有的点都被连接,任意两个点之间最多有一条,不存在连接自身的),每条光缆有一定的价值,网络中1为起点,n为终点,现在要求找出一些光缆能分割开1到n,使它们不能相互通信,并且要求花费的和除以光缆数的值最小。输出选择的光缆的编号。
从问题中可以看出一定是0-1分数规划的题目,假设选出光缆的集合M,M为原图的一个割,光缆si∈M,价值为ci,数量k = 1 ,可以推出g(x) = min( ∑c - x*∑k ),因为si是割中的边,将边的值转化为ci-x*k,那么g(x)为原图的最小割。这样就得到了单调关系,对于x的值进行二分,求最小割。求得当g(x)为0的时候的x,也就是花费的和除以光缆数的值最小。
求到x值以后,从1点进行搜索,找出所有能走到的点。如果一条边的两个点,一个被遍历到,一个没有被遍历到,那么这条边为一条割边。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std ;
#define eqs 1e-9
#define INF 0x3f3f3f3f
struct node{int u , v ;double w ;int next , id ;
}edge[2100] , p[600] ;
int head[110] , cnt , id[110][110] ;
int n , m , l[110] , vis[110] , flag[600] , num ;
double sum[110][110] ;
queue <int> que ;
vector <int> vec ;
void add(int u,int v,double w,int id) {edge[cnt].u = u ; edge[cnt].v = v ; edge[cnt].w = w ;edge[cnt].next = head[u] ; edge[cnt].id = id ; head[u] = cnt++ ;edge[cnt].u = v ; edge[cnt].v = u ; edge[cnt].w = 0 ;edge[cnt].next = head[v] ; edge[cnt].id = id ; head[v] = cnt++ ;
}
int bfs(int s,int t) {int u , v , i ;memset(l,-1,sizeof(l)) ;while( !que.empty() ) que.pop() ;que.push(s) ;l[s] = 0 ;while( !que.empty() ) {u = que.front() ;que.pop() ;for(i = head[u] ; i != -1 ; i = edge[i].next) {v = edge[i].v ;if( l[v] == -1 && edge[i].w >= eqs ) {l[v] = l[u] + 1 ;que.push(v) ;}}}if( l[t] > 0 ) return 1 ;return 0 ;
}
double dfs(int u,int t,double min1) {if( u == t ) return min1 ;int v , i ;double temp , ans = 0 ;for(i = head[u] ; i != -1 ; i = edge[i].next) {v = edge[i].v ;if( l[v] == l[u]+1 && edge[i].w >= eqs && ( temp = dfs(v,t,min(min1,edge[i].w) ) ) ) {edge[i].w -= temp ;edge[i^1].w += temp ;ans += temp ;min1 -= temp ;if( min1 < eqs ) break ;}}if( ans >= eqs ) return ans ;l[u] = -1 ;return 0 ;
}
double solve(double mid) {int i , j ;double ans = 0 , temp ;memset(head,-1,sizeof(head)) ;memset(flag,0,sizeof(flag)) ;cnt = num = 0 ;for(i = 0 ; i < m ; i++) {if( p[i].w - mid < 0 ){flag[i] = 1 ;num++ ;ans += p[i].w-mid ;continue ;}add(p[i].u,p[i].v,p[i].w-mid,i) ;add(p[i].v,p[i].u,p[i].w-mid,i) ;}while( bfs(1,n) ) {while( (temp = dfs(1,n,INF) ) >= eqs )ans += temp ;}return ans ;
}
void f_dfs(int u) {int i , v ;for(i = head[u] ; i != -1 ; i = edge[i].next) {v = edge[i].v ;if( vis[v] || edge[i].w < eqs ) continue ;vis[v] = 1 ;f_dfs(v) ;}
}
void f() {int i , u , v ;memset(vis,0,sizeof(vis)) ;vis[1] = 1 ;f_dfs(1) ;for(i = 0 ; i < cnt ; i += 2) {u = edge[i].u ; v = edge[i].v ;if( vis[u] && !vis[v] && edge[i].w < eqs && !flag[ id[u][v] ] ) {flag[ id[u][v] ] = 1 ;num++ ;}}
}
int main() {int i , j ;int u , v ;double w , low , mid , high , temp ;while( scanf("%d %d", &n, &m) != EOF ) {low = mid = high = 0.0 ;memset(head,-1,sizeof(head)) ;cnt = 0 ;for(i = 0 ; i < m ; i++) {scanf("%d %d %lf", &p[i].u, &p[i].v, &p[i].w) ;id[ p[i].u ][ p[i].v ] = id[ p[i].v ][ p[i].u ] = i ;high += p[i].w ;}while( high - low >= eqs ) {mid = (high + low) / 2.0 ;temp = solve(mid) ;if( fabs(temp) < eqs ) break ;if( temp < 0 )high = mid ;elselow = mid ;}f() ;printf("%d\n", num) ;for(i = 0 ; i < m && num ; i++) {if( !flag[i] ) continue ;num-- ;if( num ) printf("%d ", i+1) ;else printf("%d\n", i+1) ;}printf("\n") ;}return 0 ;
}
这篇关于zoj2676--Network Wars(0-1分数规划+最小割)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!