zoukankan      html  css  js  c++  java
  • 洛谷 P3199 [HNOI2009]最小圈(01分数规划,spfa判负环)

    传送门


    解题思路

    概括一下题意:求min(sumw/sumn),其中sumw表示一个环上的边权和,sumn表示一个环上点的数量。

    这种分数规划问题很常见的一个套路为二分答案,然后转化成01分数规划。

    即二分一个比值k,判断有无环满足sumw/sumn<=k,也就是sumw-sumn*k<=0。进一步整理一下,得到:

    [sum_{i=1}^{n}(w-k)leq 0 ]

    于是我们就可以把所有的边权减去k,然后判断有无负环!
    spfa一遍即可。
    这个题bfs版的会被卡死,需要写dfs版的。

    AC代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<iomanip>
    using namespace std;
    const int maxn=4005;
    const double inf=1e9+5;
    const double eps=1e-9;
    int n,m,cnt,p[maxn],vis[maxn];
    double dis[maxn],l=-1e7,r,maxw,mid;
    struct node{
    	int v,next;
    	double w;
    }e[30005];
    void insert(int u,int v,double w){
    	cnt++;
    	e[cnt].v=v;
    	e[cnt].w=w;
    	e[cnt].next=p[u];
    	p[u]=cnt;
    }
    void recover(double x){
    	for(int i=1;i<=m;i++){
    		e[i].w+=x;
    	}
    }
    bool spfa(int u){
    	vis[u]=1;
    	for(int i=p[u];i!=-1;i=e[i].next){
    		int v=e[i].v;
    		if(dis[v]>=dis[u]+e[i].w){
    			dis[v]=dis[u]+e[i].w;
    			if(vis[v]||!spfa(v)) return 0;
    		}
    	}
    	vis[u]=0;
    	return 1;
    }
    bool check(double x){
    	for(int i=1;i<=m;i++) e[i].w-=x;
    	memset(dis,0,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	for(int i=1;i<=n;i++){
    		if(!spfa(i)){
    			recover(x);
    			return 1;
    		}
    	}
    	recover(x);
    	return 0;
    }
    int main(){
    	ios::sync_with_stdio(false);
    	memset(p,-1,sizeof(p));
    	cin>>n>>m;
    	for(int i=1;i<=m;i++){
    		int u,v;double w;
    		cin>>u>>v>>w;
    		insert(u,v,w);
    		maxw=max(w,maxw);
    	}
    	r=maxw;
    	while(r-l>eps){
    		double mid=(l+r)/2;
    		if(check(mid)) r=mid;
    		else l=mid;
    	}
    	cout<<fixed<<setprecision(8)<<l;
    	return 0;
    }
    

    另一种做法

    _rqy学姐提供了另一种不会被卡的做法:
    直接放博客链接https://www.cnblogs.com/y-clever/p/7043553.html
    这样就不用再看出题人脸色了

  • 相关阅读:
    【转】进程与线程的一个简单解释
    折半查找
    BOJ 89 统计时间间隔
    BOJ 88 最值问题
    BOJ 87 日期
    QT杂记(网上资料整理而成)
    我的博客园
    【制作镜像】virsh
    【培训】MySQL
    ERROR 1045 (28000): Access denied for user 'root'@'localhost'
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/15349788.html
Copyright © 2011-2022 走看看