bzoj2165: 大楼(倍增floyd)

  题目大意:一个有向图,n(<=100)个点求一条长度>=m(<=10^18)的路径最少经过几条边。

  一开始以为是矩乘,蓝鹅当时还没开始写,所以好像给CYC安利错了嘿嘿嘿QWQ

  第一眼看到这题就想到了某题(戳我),最后只想出一半。。。

  倍增floyd:f[p][i][j]表示走了2^p条边,从i到j的最长路径,然后则有:f[p][i][j]=max(f[p][i][j],f[p-1][i][k]+f[p-1][k][j]);

  当跑倍增floyd的时候,1到某个点路径长度>=m则记录p后break。但是这个p只能告诉你答案在2^(p-1)~2^p之间,那到底怎么统计最少经过几条边儿呢?这就是我当时没想出来的>_<。。。

  看了题解,ciao,sb了QAQ。。。把这个p按高位到低位贪心,新增一个g[i][j]数组表示当前i到j的最长路径,如果1到某个点长度>=m就不记录,否则每次用f[p][i][k]+g[k][j]来更新g数组,并给ans+=1<<p,这样就可以统计出路径<m的答案了,这个时候随便走一步就>=m,所以输出ans+1就行辣!

  初始化-233333333不够小害我WA了好几次QAQ【于是我就改成了-2333333333333333333嘿嘿嘿

  然后还学会了新姿势!

try
{
    throw(true);
}catch(bool){}
View Code

代码如下:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
int n,p,T;
ll f[61][110][110],h[110][110],g[110][110],ans,m,x;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d%lld",&n,&m);
        memset(f,0xef,sizeof(f));
        memset(g,0xef,sizeof(g));  
        for(int i=1;i<=n;i++)g[i][i]=0;  
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%lld",&x),f[0][i][j]=x?x:-2333333333333333333;  
        try
        {
            for(p=1;p<=61;p++)
            for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                f[p][i][j]=max(f[p][i][j],f[p-1][i][k]+f[p-1][k][j]);
                if(i==1&&f[p][i][j]>=m)throw(true);
            }
        }catch(bool){}
        while(p--)
        {
            for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i==j)h[i][j]=0;else h[i][j]=-233333333;
            try
            {
                for(int k=1;k<=n;k++)
                for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                {
                    h[i][j]=max(h[i][j],f[p][i][k]+g[k][j]);
                    if(i==1&&h[i][j]>=m)throw(true);
                }
                memcpy(g,h,sizeof(g));
                ans+=1ll<<p;
            }catch(bool){}
        }
        printf("%lld\n",ans+1);
    }
}
View Code

 

转载于:https://www.cnblogs.com/Sakits/p/6535820.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值