题解P1613跑路

今天没什么好说的那我就卖个萌吧(~ ̄▽ ̄)~

Luogu

一道思维题可能仅是对我来说,对于大佬们都是显然的


简化题意

给你一个图,找到一个路径,使其路径的二进制位中\(1\)的个数最少

分析

这题的题目就有两个坑点或许仅是对我而言

  1. 每次走的时候,只有在端点才可以停下,即如果有一条路从\(i\)\(j\),如果其路径长为7,则不可以从\(i\)直接走到\(j\),你要走\(3\)次。

  2. 由于\(1.\)所以直接跑最短路不一定是最优。

然后想解法

首先,由于~~标签/路径太长了/牛*的~~机器特殊的跑路方式,所以我们想到了用倍增来解决这个问题

然后考虑这题最后还是要求最短路。我们又看到了数据范围\(n<=50\),所以就想到弗洛伊德,求最短路。但由于上面的结论,我们不可以直接跑最短路,故而我们需要对这个图的做一个转化。这道题很难对图的点做出转化,所以我们选择对边/路径做出转化。

然后就考虑对路径的转化。考虑之前不能直接跑最短路的原因:我们无法在较短的时间里直接确定怎样的路径中二进制位的\(1\)的个数更少,所以当我们把路径的贡献转化为它在二进制中\(1\)的个数你就可以算的最短路。

所以先预处理出从\(i\)\(j\)的最短路径中\(1\)的个数,然后再求一边最短路就好的。

对于求\(i\)\(j\)的最短路径中\(1\)的个数,由于\(i\)\(j\)的路径有一种性质我哦不知道具体的名字\(i\)\(t\)中个数为\(k\),\(t\)\(j\)中个数为\(k\),那么\(i\)\(j\)中的\(1\)的个数就是\(k+1\)。故,我们可以用类似于传递闭包的方式来求的每两个点之间的最短路中的二进制位的\(1\)的个数


code

#include<iostream>
#include<cstring>
using namespace std;
int n,m,x,y,dis[55][55];
bool map[55][55][65];
inline void pre(){
    for(register int t=0;t<=64;t++)
        for(register int k=1;k<=n;k++)
            for(register int i=1;i<=n;i++)
                for(register int j=1;j<=n;j++)
                    if(map[i][k][t-1]&&map[k][j][t-1])
                        map[i][j][t]=1,dis[i][j]=1;
}
inline void floyed(){
    for(register int k=1;k<=n;k++)
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=n;j++)
                dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
}
int main(){
    scanf("%d%d",&n,&m);
    memset(dis,0x3f,sizeof(dis));
    for(register int i=1;i<=m;i++)
        scanf("%d%d",&x,&y),
        dis[x][y]=1,map[x][y][0]=1;
    pre();floyed();printf("%d",dis[1][n]);
}

最后国际惯例,thankyou for your attention

猜你喜欢

转载自www.cnblogs.com/fallen-down/p/11589151.html
今日推荐