POJ3621 Sightseeing Cows(最优比率环)(分数规划)(SPFA求负环)

版权声明:转载请声明出处,谢谢支持 https://blog.csdn.net/Dreamstar_DS/article/details/82825406

题目链接:https://vjudge.net/problem/POJ-3621
(个人比较喜欢用vjudge)

顺便贴个LG3385负环板题(无向图)的代码,以供参考

#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 200005
#define ll long long
#define lid id << 1
#define rid (id << 1) | 1
#define rep(a,b,c) for (rg int a = 1 ; a <= c ; a += b)
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
int head[maxn] ,  cnt , n , m , num[maxn] , q[maxn << 5] , dis[maxn];
bool vis[maxn];
struct edge{
    int fr , to , next , v;
}e[maxn << 1];
void add(int u,int v,int w){
    e[++cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
    e[cnt].v = w;
}
bool spfa(int s){
    rg int l = 1 , r = 0;
    memset(vis , 0 , sizeof(vis));
    memset(num , 0 , sizeof(num));
    memset(dis , 127 , sizeof(dis));
    dis[s] = 0;
    vis[s] = 1;
    q[++r] = s;
    num[s] = 1;
    while (l <= r){
        rg int now = q[l];
        ++l;
        vis[now] = 0;
        for (rg int i = head[now] ; i ; i = e[i].next){
            rg int to = e[i].to;
            if (dis[to] > dis[now] + e[i].v){
                dis[to] = dis[now] + e[i].v;
                if (!vis[to]){
                    num[to]++;
                    if (num[to] > n) return 0;
                    q[++r] = to;
                    vis[to] = 1;	
                }
            }
        }
    }
    return 1;
}
int main(){
    rg int t = read();
    while (t--){
    	n = read() , m = read();
        rg int u , v , w;
        cnt = 0;
        memset(head , 0 , sizeof(head));
   		for (rg int i = 1 ; i <= m ; ++i){
 	   		u = read() , v = read() , w = read();
    		add(u , v , w);
    		if (w >= 0 ) add(v , u , w);
    	}
   		bool flag = spfa(1);
    	if (flag) puts("N0");//真坑
    	else puts("YE5");//真坑
    }
    return 0;
}

新学了这类问题,了解到分数规划问题不少都是采用二分的方法,像此题我们是求所有环中sum{T[i] / F[i]}的MAX,我们就设k为二分答案,那么原式可以表示为sumT[i] - k * sumF[i] >= 0 , 由于两处都是sum,就相当于判断边权的正负性,就相当于我们要找一个正环。
正环咋找啊,我们将边权取负,不就是SPFA找负环了吗(interestring)

下面当然还有double二分的写法,都取mid,写eps等等

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define rg register
#define il inline
#define maxn 200005
#define ll long long
#define eps 1e-8
#define lid id << 1
#define rid (id << 1) | 1
#define rep(a,b,c) for (rg int a = 1 ; a <= c ; a += b)
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
int head[maxn] ,  cnt , n , m , num[maxn] , q[maxn << 5] , p[maxn];
double val[maxn];
double dis[maxn];
bool vis[maxn];
struct edge{
    int fr , to , next;
	double v;
}e[maxn << 1];
void add(int u,int v,double w){
    e[++cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
    e[cnt].v = w;
    val[cnt] = w;
}
bool spfa(int s){
	rg int l = 1 , r = 0;
	memset(vis , 0 , sizeof(vis));
	memset(num , 0 , sizeof(num));
	memset(dis , 127 , sizeof(dis));
	dis[s] = 0;
	vis[s] = 1;
	q[++r] = s;
	num[s] = 1;
	while (l <= r){
		rg int now = q[l];
		++l;
		vis[now] = 0;
		for (rg int i = head[now] ; i ; i = e[i].next){
			rg int to = e[i].to;
			if (dis[to] > dis[now] + e[i].v){
				dis[to] = dis[now] + e[i].v;
				if (!vis[to]){
					num[to]++;
					if (num[to] > n) return 1;
					q[++r] = to;
					vis[to] = 1;	
				}
			}
		}
	}
	return 0;
}
bool check(double mid){
    for (rg int i = 1 ; i <= m ; ++i) e[i].v = -p[e[i].to] + mid * val[i];//边权取负 
    return spfa(1);		
}
int main(){
    	n = read() , m = read();
		rg int u , v , w;
		cnt = 0;
		memset(head , 0 , sizeof(head));
		for (rg int i = 1 ; i <= n ; ++i)
			p[i] = read();
   		for (rg int i = 1 ; i <= m ; ++i){
 	   		u = read() , v = read() , w = read();
    		add(u , v , (double)w);
    		//if (w >= 0 ) add(v , u , (double)w);
    	}
		double l = 0 , r = 10000 , ans;
		while (r - l >= eps){
			rg double mid = (l + r) / 2;
			if (check(mid)) l = mid;
			else r = mid;
		}
		printf("%.2lf\n" , l);
	
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Dreamstar_DS/article/details/82825406