首页 > 代码库 > 【bzoj1486】[HNOI2009]最小圈 分数规划+Spfa
【bzoj1486】[HNOI2009]最小圈 分数规划+Spfa
题目描述
样例输入
4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3
样例输出
3.66666667
题解
分数规划+Spfa判负环
二分答案mid,并将所有边权减去mid,然后再判负环,若有负环则调整下界,否则调整上界,直至上下界基本重合。
证明:显然
由于有(c+d)/(a+b+k)>(c+d)/(a+b)≥min(c/a,d/b),所以两个相交环形成的新环一定不是最优解,即答案一定是简单环。
如果存在环使得边权和/点数<mid,那么就有边权和<点数*mid。
又因为环中点数和边数相等,所以有边权和小于边数*mid,移项即得:存在负环。
这个时候需要调整上界,进一步更新答案;否则不存在则调整下界。
然后这道题的坑点:需要用dfs版Spfa判负环,据说bfs版会T,不明觉厉。
#include <cstdio>#include <cstring>#include <algorithm>#define eps 1e-9#define N 3010#define M 10010using namespace std;int n , m , head[N] , to[M] , next[M] , cnt , x[M] , y[M] , vis[N];double len[M] , dis[N] , z[M];void add(int x , int y , double z){ to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;}bool dfs(int x){ int i; vis[x] = 1; for(i = head[x] ; i ; i = next[i]) { if(dis[to[i]] > dis[x] + len[i]) { dis[to[i]] = dis[x] + len[i]; if(vis[to[i]]) return 1; if(dfs(to[i])) return 1; } } vis[x] = 0; return 0;}bool judge(double mid){ int i; memset(head , 0 , sizeof(head)); memset(vis , 0 , sizeof(vis)); memset(dis , 0 , sizeof(dis)); cnt = 1; for(i = 1 ; i <= m ; i ++ ) add(x[i] , y[i] , z[i] - mid); for(i = 1 ; i <= n ; i ++ ) if(dfs(i)) return 1; return 0;}int main(){ int i; double l = 100000000.0 , r = -100000000.0 , mid; scanf("%d%d" , &n , &m); for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%lf" , &x[i] , &y[i] , &z[i]) , l = min(l , z[i]) , r = max(r , z[i]); while(l <= r) { mid = (l + r) / 2; if(judge(mid)) r = mid - eps; else l = mid + eps; } printf("%.8lf\n" , (l + r) / 2); return 0;}
【bzoj1486】[HNOI2009]最小圈 分数规划+Spfa
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。