POJ 3613 Cow Relays Floyd+快速幂加速

来源:互联网 发布:configparser python 编辑:程序博客网 时间:2024/06/05 11:24

题目大意:求从S到T 恰好经过k条边的最短路。(2<=k<=1000000)

解题思路:最开始看到题目以为是K短路,不过看到K的数量级就明显不适合了。

根据题意,既有最短路,k这么大,时间限制还是1s,便想到去优化。

然后看到点数为1000,而边数最多只有100,先进行一步离散化,不过复杂度还是不够,然后就开始考虑这道题的特殊性了。


K最多为1000,000,而边数只有一百,可以预见中间有许多状态是重复出现的,甚至有可能在一个环中不停地走。

于是考虑把中间重复的计算给优化一下。便想到了快速幂优化和ST算法。(这两个都是能基于重复的状态进行优化)

而便于重复状态的记录的话,用矩阵来存储状态是再合适不过的了,由此想到最短路用Floyd来求。


先简述一下快速幂优化的思想:

如果想求K^a, 假设a=43.  我们把a转化为二进制便是 101011

接下来表示时我们把指数都用二进制表示,原式便是求 K^(101011) ,

=K^(1<<0) + K^(1<<1) + K^(1<<3) +K^(1<<5) , 

而K^(1<<1)= (K^(1<<0))^2 , K^(1<<2)= (K^(1<<1))^2 , K^(1<<3)= (K^(1<<2))^2 ,K^(1<<4)= (K^(1<<3))^2 ,K^(1<<5)= (K^(1<<4))^2 ....

也就是说每一位都可以由上一位的平方得到,原来的指数级运算便成了加法运算..

而只要是1e9级以内的指数运算我们都可以在32次运算内完成....( 很强大... ) (  当然...不要给我说数据溢出什么的...那种题一般要取余,取余方法自己找快速幂看 )


再回到这题,用Floyd算法的话,

假设从a到b经过恰好经过 (1<<i) 条路的最短路是len[a][b][ 1<<i ]=min(len[ a ][ c ][ 1<<(i-1) ] + len(len[ c ][ b ][ 1<<(i-1) ]

哪怕是经过101011条路径的最短路我们也都可以像上面的优化一样依次求出。

具体实现看代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define FOR(i,l,r) for(int i=(l);i<=(r);++i)#define REP(i,n) for(int i=0;i<(n);++i)#define N 1010#define INF 1e9#define min(a,b) (a<b?a:b)int map[2][N][N];int ans[2][N][N];int f[N];bool flag[N];void floyd(int num,int cur,int pre){    REP(i,num)        REP(j,num)            map[cur][f[i]][f[j]]=INF;    REP(k,num)        REP(i,num)            REP(j,num)                map[cur][f[i]][f[j]]=min(map[cur][f[i]][f[j]],map[pre][f[i]][f[k]]+map[pre][f[k]][f[j]]);}int main(){    int n,m,start,end;    int u,v,c;    while(cin>>n>>m>>start>>end)    {        memset(flag,0,sizeof(flag));        FOR(i,1,1000)            FOR(j,1,1000)                map[0][i][j]=INF; //初始化        int num=0;        while(m--)        {            scanf("%d%d%d",&c,&u,&v);            map[0][u][v]=map[0][v][u]=c;            if(!flag[u]) f[num++]=u,flag[u]=1; //离散化            if(!flag[v]) f[num++]=v,flag[v]=1; //离散化        }        REP(i,num)        {            REP(j,num)            ans[0][f[i]][f[j]]=INF;            ans[0][f[i]][f[i]]=0;   //初始化        }        int cur=0,pre=1;        int temp1=0,temp2=1;           //temp1是当前状态,temp2是之前的状态        //因为map[1<<i][a][b] 只与 map[1<<(i-1)][x][y] 有关        //所以这两个数组能循环利用        //没必要开32位,开两位足以,是一种空间上的优化        while(n)        {            temp1^=1,temp2^=1;            floyd(num,temp1,temp2);//i从0开始,在进行第i次while()循环时,求出从a到b恰好经过(1<<i)条边的最短路是多少            if(n&1) //如果当前位为1才进行计算,把ans进行更新            {                cur^=1,pre^=1;//和temp1,temp2的效果是一样的                REP(i,num)                    REP(j,num)                        ans[cur][f[i]][f[j]]=INF;//只用对离散化后的点初始化                REP(k,num)                    REP(i,num)                        REP(j,num)                            ans[cur][f[i]][f[j]]=min(ans[cur][f[i]][f[j]],ans[pre][f[i]][f[k]]+map[temp2][f[k]][f[j]]);                            //在离散化后的点中进行Floyd            }            n>>=1;        }        cout<<ans[cur][start][end]<<endl;    }    return 0;}





原创粉丝点击