BZOJ 2165 大楼 倍增Floyd

3 篇文章 0 订阅

题目大意:给出一个有向图,问总路径长度>=k最少需要经过多少边。


思路:记录几个辅助数组,f[p][i][j]表示走2^p步时最长的路径是多少。g[i][j]表示目前从i到j最长路是多长。

f的dp方程是f[p][i][j] = max(f[p][i][j],f[i - 1][i][j] = f[i - 1][j][k]);

然后在处理g数组的时候当从1开始的最长路大于等于k的时候就直接break掉,这个时候不计入总答案,否则计入总答案。

这样就处理出了总长度<k的答案,任意再走一步就可以得到答案了。


CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 110
using namespace std;
 
int T,points;
long long f[70][MAX][MAX],g[MAX][MAX],t[MAX][MAX],len,ans;
 
inline void Initialize()
{
    ans = 0;
    memset(f,0xef,sizeof(f));
    memset(g,0xef,sizeof(g));
    for(int i = 1; i <= points; ++i)
        g[i][i] = 0;
}
 
int main()
{ 
    for(cin >> T; T--;) {
        scanf("%d%lld",&points,&len);
        Initialize();
        for(int i = 1; i <= points; ++i)
            for(int j = 1; j <= points; ++j) {
                scanf("%lld",&f[0][i][j]);
                if(!f[0][i][j]) f[0][i][j] = 0xefefefefefefefefll;
            }
        int p = 1;
        try{
            for(; 1ll << p <= len; ++p)
                for(int k = 1; k <= points; ++k) 
                    for(int i = 1; i <= points; ++i)
                        for(int j = 1; j <= points; ++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] >= len)
                                throw(true);
                        }
        }
        catch(bool) {}
        while(p--) {
            memset(t,0xef,sizeof(t));
            try{
                for(int k = 1; k <= points; ++k)
                    for(int i = 1; i <= points; ++i)
                        for(int j = 1; j <= points; ++j) {
                            t[i][j] = max(t[i][j],f[p][i][k] + g[k][j]);
                            if(i == 1 && t[i][j] >= len)
                                throw(true);
                        }
                memcpy(g,t,sizeof(g));
                ans += 1ll << p;
            }
            catch(bool) {}
        }
        printf("%lld\n",ans + 1);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值