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 ;
            else
                low = 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 ;
}
时间: 2024-10-20 23:46:32

zoj2676--Network Wars(0-1分数规划+最小割)的相关文章

HDU 2676 Network Wars 01分数规划,最小割 难度:4

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1676 对顶点i,j,起点s=1,终点t=n,可以认为题意要求一组01矩阵use[i][j],使得aveCost=sigma(use[i][j]*cost[i][j])/sigma(use[i][j])最小,且{(i,j)|use[i][j]==1}是图的S-T割 定义F(e)=min(sigma(use[i][j]*(cost[i][j]-a))),明显,F(e)是目标式的变

【BZOJ3232】圈地游戏 分数规划+最小割

[BZOJ3232]圈地游戏 Description DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用. DZY喜欢在地里散步.他总是从任意一个格点出发,沿着格线行走直到回到出发点,且在行走途中不允许与已走过的路线有任何相交或触碰(出发点除外).记这条封闭路线内部的格子总价值为V,路线上的费用总和为C,DZY想知道V/C的最大值是多少. Input 第一行为两个正整数n,m. 接下来n行,每行m个非负整数,表示对应格子的价值. 接下来n

ZOJ 2676 Network Wars(最优比例最小割)

Network Wars Time Limit: 5 Seconds      Memory Limit: 32768 KB      Special Judge Network of Byteland consists of n servers, connected by m optical cables. Each cable connects two servers and can transmit data in both directions. Two servers of the n

bzoj 3232: 圈地游戏【分数规划+最小割】

数组开小导致TTTTTLE-- 是分数规划,设sm为所有格子价值和,二分出mid之后,用最小割来判断,也就是判断sm-dinic()>=0 这个最小割比较像最大权闭合子图,建图是s像所有点连流量为格子价值的边(相当于最大权闭合子图中的正权点),然后考虑边缘,两个相邻的格子,如果一个选一个不选那么中间这条边就有负的贡献,所以两个相邻的格子之间连两条边权为mid*边权的边,注意是两条,要互相连一下,然后所有边界上的点像t连边权为mid*边界边权的边,相当于假装外面还有一层点全标为t,然后跑最小割判断

【POJ3155】Hard Life 分数规划+最小割

链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/46437961"); } 题解: 如题.先算出那个分数值,然后看有哪些人还与源点相连. 最小割建图:原图每个点对应一个点,原图每条边对应一个点.每条边对应点向两端点对应点连边,注意要单向边. 这道题卡精度: 所以一些细

0/1分数规划

学习了lyd书上的0/1分数规划,发现这类题目都有一个特点,就是求$\frac{\sum_{a_{i}*x_{i}}}{\sum_{b_{i}*x_{i}}}$的最大或者最小,再加一些限制取不取的条件. POJ2976 二分答案+sort取前(n-k+1)个. #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; con

2019.4.9 一题——概率期望+0/1分数规划+最大权闭合子图

没注意 “第 x 条边和第 y 条边的起点是相同的” 的限制.没想出来. 有这个限制,可以考虑每个点分别计算.令 \( f[i] \) 表示从 i 出发的最大边数期望,那么先把拓扑序在自己之后的点的 \( f[ ] \) 算出来,然后考虑自己这个点的出边怎么做能使自己的 \( f[ ] \) 最大. \( f[i]=\frac{ \sum f[j]+1 }{ d } \) ,其中 d 是保留下来的边数, j 是保留边指向的点. 如果把 \( f[ ]+1 \) 看做收益, 1 看做代价,那么这个

『0/1分数规划 二分法』

0/1分数规划 模型 0/1分数规划指的是这样一个问题模型: 给定整数\(a_1,a_2,...,a_n\)和\(b_1,b_2,...,b_n\),求一组解\(x_1,x_2,...,x_n(\forall\ i\in[1,n],x_i=1,0)\),使得下式最大化:\[\frac{\sum_{i=1}^na_i*x_i}{\sum_{i=1}^nb_i*x_i}\] 简单地说,就是给定\(n\)对整数\(a_i,b_i\),从中选取若干对,使得选出的\(a\)之和与\(b\)之和的比值最大.

[例题/总结]0/1分数规划

[TOC] ##一.总述 0/1分数规划是专门解决0/1分数规划模型的一种算法~~(废话)~~.所以说0/1分数规划模型是什么呢?给定整数{\(a_1,a_2,a_3,...,a_n\)},{\(b_1,b_2,b_3,...,b_n\)}从中选出若干对数,使得它们各自和的比值最大.公式如下: \(\frac{\sum_{p=1}^{n}a_p\times x_p}{\sum_{p=1}^{n}b_p\times x_p}(x_p=1,0)\) ##二.实现原理 那么我们用什么方法可以求出这样一