sgu 236 输出负环回路 367页

题意:给出一个有向图,每条边有一个权值和时间花费。如果图中无环,输出0,如果有环,找出一个最大的环,最大的意思是环上的权和 / 时间和 最大。

如果存在负环,那么就是原式 tot(time)*k - tot(cost) < 0 成立,找到了一个解,那么就可以试着把k调大,因为我们要tot(cost) / tot(time) 最大,这样就是一个二分答案的过程

const  int  maxn = 58 ;
int     m   , n  ;
struct  Edge{
        int  u , v ;
        double  c , t ;
        void read(){
             scanf("%d%d%lf%lf" , &u , &v , &c ,&t) ;
        }
}e[maxn * maxn] ;

const   double   inf = 1e10 ;
const   double   eps = 1e-10 ;
vector< pair<int , double> > lis[maxn] ;
double  dist[maxn] ;
int  father[maxn]  ;
bool in[maxn]  ;
int  cnt[maxn] ;

int  judge(double x){
     int i , j  ;
     for(i = 1 ; i <= n ; i++) lis[i].clear() ;
     for(i = 1 ; i <= m ; i++)
           lis[e[i].u].push_back(make_pair(e[i].v , -e[i].c + x*e[i].t)) ;
     for(i = 1 ; i <= n ; i++) dist[i] = inf ;
     memset(in , 0 , sizeof(in)) ;
     memset(cnt , 0 , sizeof(cnt)) ;
     dist[1] = 0 ;
     in[1] = 1 ;
     cnt[1] = 1 ;
     queue<int> q ;
     q.push(1) ;
     father[1] = 0  ;
     while(! q.empty()){
          int u = q.front() ;
          q.pop() ;
          in[u] = 0 ;
          if(cnt[u] > n) return u ;
          for(i = 0 ; i < lis[u].size() ; i++){
               int v = lis[u][i].first ;
               double w = lis[u][i].second ;
               if(dist[u] + w < dist[v]){
                     dist[v] = dist[u] + w  ;
                     father[v] = u ;
                     if(! in[v]){
                          cnt[v]++ ;
                          in[v] = 1 ;
                          q.push(v) ;
                     }
               }
          }
     }
     return 0 ;
}

int  main(){
     int  i , j  ;
     while(cin>>n>>m){
          for(i = 1 ; i <= m ; i++) e[i].read() ;
          double l , r , mid  , s  ;
          l = 0  , r = inf ;
          while(l + eps < r){
               mid = (l + r) * 0.5 ;
               if(judge(mid) > 0){
                    s = mid  ;
                    l = mid ;
               }
               else  r = mid ;
          }
          int u = judge(s) ;
          if(u == 0){ puts("0") ; continue  ;}
          memset(cnt , 0 , sizeof(cnt)) ;
          vector<int> ans ; ans.clear()  ;
          while(cnt[u] <= 1){
                cnt[u]++ ;
                if(cnt[u] == 2) ans.push_back(u) ;
                u = father[u] ;
          }
          printf("%d\n" , ans.size()) ;
          printf("%d" , ans[ans.size()-1]) ;
          for(i = ans.size() -2 ; i >= 0 ;  i--) printf(" %d",ans[i]) ;
          puts("")  ;
     }
     return 0 ;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值