POJ3613 Cow Relays

来源:互联网 发布:javascript 测试 编辑:程序博客网 时间:2024/05/21 17:59

Floyd-Matrix+FastEXP=Shortest-C(N)-Path

Memory: 10900K Time: 313MS
https://code.csdn.net/snippets/1631902

题意

求定源定汇的图上恰好经过N个点的最短路(经过点只包含起点和路径中的点,不包括终点,也就是经过N条边,下同)
题目中n很大(1000000),不能直接当成节点数,因为实际边数(100)很小,利用率低,所以需要离散化。而n只是具体要求的经过点数的多少。
其实求解过程并没有利用单源最短路的优势,算法跑完了,任意两点间的恰好经过N个点的最短路就都知道了

核心算法1

本题求解依赖一个变种的Floyd
原版:

for(int k=1; k<=n; ++k)    for(int i=1; i<=n; ++i)        for(int j=1; j<=n; ++j)            dis[i][j]=min(dis[i][j], dis[i][k]+dis[k][j]);

注意到,Floyd操作是直接在原邻接矩阵上进行的,也正是因此,它求出的最短路不限制经过的节点数(其实限制了<=n)

其实先循环k再循环i, j也是基于不限制经过节点数的动规思想考虑的

k相当于一个阶段,而i,j只是枚举状态
它的含义是通过求出i到j之间经过的点全部<=k的最短路推出i到j之间经过的点全部<=k+1的最短路,这个k必须放在第一重循环。
枚举一个中转点,先i->k,在k->j,这个必须从小到大,不能和下面的换。

那么如果我们不在原矩阵上进行这个操作呢?
比如,两个原始邻接矩阵m1_1, m1_2(均可视为恰好经过一个点- -就是原点 的最短路),分别放到两边,新建一个初值为inf的矩阵dis2,
跑这样的过程:

//∀dis1[i][j]=0x3f3f3f3f;for(int k=1; k<=n; ++k)    for(int i=1; i<=n; ++i)        for(int j=1; j<=n; ++j)            dis1[i][j]=min(dis1[i][j], m1_1[i][k]+m1_2[k][j]);

得到的dis2,就是两个点经过一条m1的最短路,又经过一条m2的最短路的最短路径,可以想见,dis2就是两点间恰好经过两个点的最短路
如果输入的两个矩阵分别为经过i, j个点的最短路,则计算出来的矩阵就是经过(i+j)个点的最短路
这个过程实现于代码中的multiply()方法
于是求经过特定点数的最短路径的理论基础打好了

注意到这个过程很像矩阵乘法的过程,方阵乘法不过就是

//∀dis1[i][j]=0;for(int k=1; k<=n; ++k)    for(int i=1; i<=n; ++i)        for(int j=1; j<=n; ++j)            dis1[i][j]+=m1_1[i][k]*m1_2[k][j];

这里不过是把累加改成取min,乘法改成加法而已
这个特点很优美,也微弱地显示出了Floyd和线性代数的相似点,后面会用到这个相似性。

核心算法2

仅有如此,不够AC此题。因为我们如果需要经过n个点的答案矩阵,需要把邻接矩阵一个一个地“乘”进答案矩阵里,共需要乘n次,也就是1,000,000,每次复杂度和朴素矩阵乘法相同,为O(m2),m为总节点数,200。总复杂度O(nm2) 不能接受。

如果我们遇到数字乘高幂时,会用到快速幂,一分为二,充分利用答案
以前做Fibonacci那道题时,也遇到了矩阵乘幂的问题,那时就用到了矩阵快速幂
凡是满足结合律的幂运算,都可以用快速幂解决
根据刚才的分析,Floyd过程满足结合律,可以直接快速幂
这个过程实现于代码中的fastpow()方法

遇到的问题

一开始我的快速幂代码是这样的:

void fastpow(int p){    if(p==1) return;    MATRIX t1, t2, ts; ts=t1=*this;    t1.fastpow(p>>1);    t2.multiply(t1, t1);    if(p&1) this->multiply(t2, ts);    else *this=t2;    //cout<<this<<endl;}

然后出现了非常诡异的情况(⊙o⊙)?
如果把cout<<this<<endl这句话注释掉,执行代码时就爆栈SIGSEGV-3221225***
如果加上它,多输出了很多指针,但是却能正常输出答案了..

试着用-O2编译了下,发现执行正常了。然后作死交,却RE到死
分析应该是数据接近了调用栈的极限造成的(MATRIX对象很大)
既然这样,分配内存到堆里不就行了?然后我的代码变成了这样:

void fastpow(int p){    if(p==1) return;    //MATRIX t1, t2, ts; ts=t1=*this;    MATRIX *t1=new MATRIX, *t2=new MATRIX, *ts=new MATRIX;    *t1=*ts=*this;    t1->fastpow(p>>1);    t2->multiply(*t1, *t1);    if(p&1) this->multiply(*t2, *ts);    else *this=*t2;    delete t1; delete t2; delete ts;    //cout<<this<<endl;}

然后就解决了
不过内存比较高哈..
不知道有没有什么能够降低矩阵快速幂空间复杂度的方法..
待续

谨此。

0 0