--今天做了dao很脚妙的题,拿出来分享下~
题目描述
小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零。可是小A偏偏又有赖床的坏毛病。于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟可以跑2^k千米(k是任意自然数)。当然,这个机器是用longint存的,所以总跑路长度不能超过maxlongint千米。小A的家到公司的路可以看做一个有向图,小A家为点1,公司为点n,每条边长度均为一千米。小A想每天能醒地尽量晚,所以让你帮他算算,他最少需要几秒才能到公司。数据保证1到n至少有一条路径。
输入输出格式
输入格式:
第一行两个整数n,m,表示点的个数和边的个数。
接下来m行每行两个数字u,v,表示一条u到v的边。
输出格式:
一行一个数字,表示到公司的最少秒数。
输入输出样例
输入样例#1:
4 4
1 1
1 2
2 3
3 4
输出样例#1:
1
说明
【样例解释】
1->1->2->3->4,总路径长度为4千米,直接使用一次跑路器即可。
【数据范围】
50%的数据满足最优解路径长度<=1000;
100%的数据满足n<=50,m<=10000,最优解路径长度<=maxlongint。
题解:
这道题我们用到一个道路矩阵
A
(
表示
i
走一步能否到
为什么呢?
首先要注意是刚好走k步,而不是k步以内。
我们先假设
A
是
我们观察矩阵乘法的式子:
那么 i 走k步到
然而这道题只需要知道能否到达,所以就可以开成布尔型,把求和改换成或运算,乘法改换成与运算即可。
知道了道路矩阵的作用,这道题就可以动手了:
我们发现最后的决策就可以用二进制来表示,1的个数就是使用次数
我们可以把所有的
我们于是就可以建一个新图,如果存在任意
k
,使得
而且,由于边权都是1,直接用普通的BFS即可。
下面贴上脚妙至极的代码-
#include<cstdio>
#include<cstring>
#define For(a, b, c) for(int a = b; a <= c; ++a)
using namespace std;
int n;
struct Matrix{
int lx, ly;
bool x[61][61];
Matrix (int a = 0, int b = 0){
lx = a, ly = b;
memset(x, 0, sizeof x);
}
void E (){ For(i, 0, lx - 1) x[i][i] = 1; }
Matrix operator* (Matrix B){
Matrix res (lx, B.ly);
For(i, 0, lx - 1) For(j, 0, res.ly - 1) For(k, 0, ly - 1){
res.x[i][j] |= x[i][k] & B.x[k][j];
if(res.x[i][j]) break;
}
return res;
}
};
int dis[51], qu[51];
bool mp[51][51];
void BFS (){
For(i, 1, n) dis[i] = -1;
dis[qu[1] = 1] = 0;
int f = 0, l = 1;
while(f++ < l){
For(v, 1, n) if(mp[qu[f]][v]){
if(~dis[v]) continue;
dis[v] = dis[qu[f]] + 1;
qu[++l] = v;
}
if(~dis[n]) return ;
}
}
int main (){
#ifndef ONLINE_JUDGE
freopen("pro.in", "r", stdin);
freopen("pro.out","w",stdout);
#endif
int m, u, v;
scanf("%d%d", &n, &m);
Matrix A (n, n);
For(i, 1, m){
scanf("%d%d", &u, &v);
A.x[u - 1][v - 1] = 1;
}
For(i, 0, 64){
For(u, 0, n - 1) For(v, 0, n - 1)
if(A.x[u][v] && u != v) mp[u + 1][v + 1] = true;
A = A * A;
}
BFS();
printf("%d", dis[n]);
return 0;
}