写在前面:
记得之前数学老师说,一张试卷不订正错题就等于一张废弃的毫无意义的试卷。突然意识到算法题也是如此,遂决定每场比赛好好补题。
F moving on
一道图论题,简单来说就是在有约束条件下跑最短路。给的数据是邻接矩阵,我们刚开始想的是跑Floyd,可能是因为对Floyd算法理解不够深刻,以为每一次query都要跑一遍Floyd(头都跑掉了好不啦 ),就开始写dij,最后好像我和飞哥慢慢接近了正确答案不过还是因为对算法理解有欠缺,没有过掉。
不过补完这个题感觉整个世界都清明了,对Floyd也有了更深刻的理解,再次在赛场上遇到这种题,应该是可以写出转移方程然后过掉了吧。
众所周知,Floyd有三层循环,之前其实一直没有很理解最上层关于k的循环,只知道Floyd是根据中间结点进行更新云云。后来数据结构课上跟帆神讨论才知道,k层的意义就是确定一个中间结点,然后根据这个中间结点来更新整个图。其中k只起到一个计数的作用,k所代表的那个中间结点在最朴素的Floyd中是没有顺序的(或者说是原始顺序),而现在在我们有了约束条件后,我们可以根据我们的约束条件来赋予每次更新点我们需要的值,这样就充分利用了第一层循环。
总结来说:如果在求取任意两点间最短路,而这个约束条件也是有一定顺序约束的(比如说小于某个值的时候,或者大于某个值的时候),就可以使用在第一层约束下的Floyd。超nice的解法。
说到这里这一题也就呼之欲出了,对所有节点的危险程度进行排序,然后跑k层时将第k小的危险程度所对应的的点所谓更新点进行dp即可。
需要注意的是,由于更新是基于在上一次的情况下跑不跑k这个点,就会涉及到k-1层的问题,所以在三维Floyd的情况下就要把所有循环设为从1开始。
nothing more to say , show the code
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
// 3-d floyd should begin with the index 1 because of the "k-1"
int dp[209][209][209];
const int INF=0x3f3f3f3f;
struct Node
{
int rob;
int id;
};
Node node[209];
bool cmp(Node a,Node b)
{
return a.rob < b.rob;
}
int main()
{
int T;
scanf("%d",&T);
for(int tt = 1;tt <= T;tt++)
{
memset(dp, INF ,sizeof dp);
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i <= n;i++)
{
scanf("%d",&node[i].rob);
node[i].id = i;
}
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
scanf("%d",&dp[0][i][j]);
}
}
sort(node+1,node+n+1,cmp);
for(int k = 1;k <= n; k++)//这个k指的是经历过k个结点后整个图的变化 k 只是一个计数器的作用//而具体到每一个k是什么才是我们需要找到的
{
int rk = node[k].id;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
dp[k][i][j] = min(dp[k-1][i][j],dp[k-1][i][rk] + dp[k-1][rk][j]);
}
}
printf("Case #%d:\n",tt);
while(q--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int k = 0;// avoid changing the original data , we should add a new variable
for(int i = n;i >= 1 ;i--)
{
if(node[i].rob <= z)
{
k = i;
break;
}
}
printf("%d\n",dp[k][x][y]);
}
}
return 0;
}