您的位置:首页 > 其它

2165: 大楼 倍增floyd

2016-01-08 08:26 211 查看
虽然A掉了……但还是有点不明白的地方QAQ,我还是太弱了。

首先倍增floyd或矩阵乘法无误(实质上是差不多的),用f[p][i][j]表示走了2^p步从第i个房间到第j个房间最多上升了几层,转移显然:f[p][i][j]=max(f[p-1][i][k]+f[p-1][k][j])。

当存在f[p][i][j]>=m时,即出现了合法的解,但此时的意义是:走了2^p步到了m层或以上的地方,但这2^p步中可能存在从m层以上继续往上走的情况,而根据题目这些步其实是冗余的,我们应将其去掉。于是我们可以用g[i][j]表示目前从i到j最长路是多长,注意这个定义和f是有区别的,g[i][j]并没有步数的限制,那么我们在枚举p的时候,如果当前的MAX(f[p][i][k]+g[k][j])>=m,我们就break,否则就加上1 << p,这样最后得到的答案是一个接近于m但小于m的值,我们再走一步即可到达m及以上,所以输出ans+1。

[code]#include<bits/stdc++.h>
#define ll long long
using namespace std;
int T,n,p;
bool flag;
ll f[70][105][105],g[105][105],t[105][105],ans,m;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void work1()
{
    for (p=1;(1ll<<p)<=m;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-1][i][k]+f[p-1][k][j],f[p][i][j]);
                    if (i==1&&f[p][i][j]>=m) return;
                }
}
inline void work2()
{
    memset(t,0xef,sizeof(t));
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                t[i][j]=max(t[i][j],f[p][i][k]+g[k][j]);
                if (i==1&&t[i][j]>=m) return;
            }
    memcpy(g,t,sizeof(g));
    ans+=1ll<<p;
}
int main()
{
    T=read();
    while (T--) 
    {
        n=read(); m=read();
        memset(f,0xef,sizeof(f));
        memset(g,0xef,sizeof(g));
        for (int i=1;i<=n;i++) g[i][i]=0;
        ans=0;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                f[0][i][j]=read();
                if (!f[0][i][j]) f[0][i][j]=-1e18;
            }
        work1();
        while (p--) work2();
        printf("%lld\n",ans+1);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: