[bzoj1486][HNOI2009]最小圈

1486: [HNOI2009]最小圈

Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1608 Solved: 752
[Submit][Status][Discuss]
Description
这里写图片描述
这里写图片描述

题解:0-1分数规划。
分数规划一般是来解决这样一种问题的:

ans=aixibixi

其中x∈{0,1},b∈R+我们要求一个ans的最小值或者最大值。
假设我们要求一个最小值
首先我们可以先将式子变形成这样:
aixi=ansbixi

aixiansbixi=0

然后很容易我们可以看出来这个关于ans的函数是一个线性的,所以可以二分。
假设我们二分到一个ans1,如果ans比ans1要更优也就是要更小,也就是
aixians1bixi<0

这样然后我们就可以根据二分出来的答案去验证一下,最后求出我们所要求的ans。最大值也一样。

然后这道题就比较简单了,我们根据上面的过程可以把要求的的东西变成

i=1kw[i][i+1]ansk=0

也是可以先二分答案,那么我们如果将每条边的边权改成w[i][i+1]-ans,这样我们从这张图上来跑最短路,如果找到了一个复权环,就说明我么当前的ans比较大,不是最优的,所以可以再取更小的值
这样用二分+SPFA判复权环就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define exp 1e-10
#define mid (l+r)/2
#define inf 1000000000
const int N=3010;
const int M=20010;
double dis[N];
bool f[N],flag;
int n,m,tot,point[N],next[M];
struct S{int st,en;double va;}aa[M],e[M];
inline void add(int x,int y,double z){
    tot+=1;next[tot]=point[x];point[x]=tot;
    aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
}
inline void SPFA(int x){
    int i;
    f[x]=false;
    for(i=point[x];i;i=next[i])
      if(dis[aa[i].en]>dis[x]+aa[i].va)
        if(!f[aa[i].en]){
            flag=true;
            break;
        }
        else{
            dis[aa[i].en]=dis[x]+aa[i].va;
            SPFA(aa[i].en);
        }
    f[x]=true;
}
inline bool check(double x){
    int i,j;
    tot=0;
    memset(next,0,sizeof(next));
    memset(point,0,sizeof(point));
    for(i=1;i<=m;++i) add(e[i].st,e[i].en,e[i].va-x);
    memset(f,1,sizeof(f));
    memset(dis,0,sizeof(dis));
    flag=false;
    for(i=1;i<=n;++i){
        SPFA(i);
        if(flag) return true;
    }
    return false;
}
int main(){
    int i,j;
    double l=-inf,r=inf;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i) scanf("%d%d%lf",&e[i].st,&e[i].en,&e[i].va);
    while(l+exp<r){
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%.8f\n",l);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值