【BZOJ】1486 [HNOI2009]最小圈

【算法】二分+spfa

【题解】据说这个叫分数规划?

0-1分数规划

二分答案a,则对于任意的环有w/k≤a即w-ak≤0,若满足条件则a变小,否则a变大。

因为w=w1+w2+...+wk,所以变形为(w1-a)+(w2-a)+...+(wk-a)≤0。于是问题转化为在图中找负环。

不过由于spfa限制,“=”没办法并入"<",但是由于精度足够,最后也就是1.00000000001≈1.00000000。

使用DFS的spfa:可以在发现更新到之前更新过的节点就认为是负环(从x跑出去最后又回来更新x,说明跑的这段路是负数)。

spfa的d数组(最短路)全部初始化为0(即只更新路径为负的),相当于设置一个起点向所有点连容量为0的边,因为是全图找负环。

确认某个曾访问的节点是祖先,这是DFS的特性和优势。

精度问题:107要求精确到10-8即log(1015)/log(2)=49,所以跑60次二分就能保证精度没问题了。

因为一个memset的size是double(以为是int)调了2小时……QAQ

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=3010,maxm=30010;
const double eps=1e-11;
struct edge{int from,v;double w;}e[maxm];
int n,m,first[maxn],tot=0;
double d[maxn],w[maxm];
bool vis[maxn],flag;
void insert(int u,int v,double w)
{tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
void spfa(int x)
{
    vis[x]=1;
    for(int i=first[x];i;i=e[i].from)
     if(!flag&&d[e[i].v]>d[x]+e[i].w)
      {
          if(vis[e[i].v])
           {
               flag=1;
               break;
           }
          d[e[i].v]=d[x]+e[i].w;
          spfa(e[i].v);
      }
    vis[x]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
     {
         int u,v;
         scanf("%d%d%lf",&u,&v,&w[i]);
         insert(u,v,w[i]);
     }
    double l=-10000000,r=10000000;
    while(r-l>eps)
     {
         double mid=(l+r)/2;
         flag=0;
         memset(d,0,8*(n+1));
         memset(vis,0,(n+1));
         for(int i=1;i<=tot;i++)e[i].w=w[i]-mid;
         for(int i=1;i<=n;i++)if(!flag)spfa(i);
         if(flag)r=mid;else l=mid;
     }
    printf("%.8lf",l);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/onioncyc/p/6723143.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值