【NOIP2014】选择道路

这题值得记录。

题目大意

在有向图 GGG 中,每条边的长度均为 1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

  1. 路径上的所有点的出边所指向的点都直接或间接与终点连通。
  2. 在满足条件  1 的情况下使路径最短。

注意:图 GGG 中可能存在重边和自环,题目保证终点没有出边。

请你输出符合条件的路径的长度。

***具体题面请看https://www.luogu.org/problemnew/show/P2296

题解

我的思路是存图的时候另开数组反向存边,然后在反向存边的图中用DFS将从终点出发能走到的点标记为1,然后一遍For循环,枚举标记为1的点的出边所到的点,如果出边所到的点中有标记为0的点,就说明当前点是不符合题目要求1的。

注意:但是我们在这里不能直接删去这个点,如果删去的话,会改变图的形态从而删去更多的点。(我就这里改了一个中午...)

所以我们再开个数组记录哪些点要删去,后统一删去即可,最后跑一遍SPFA。

错误删点方式:

   for(int i=1;i<=n;i++)
   {
        if(flag[i])
        for(int j=head[i];j;j=edge[j].next){
        int too=edge[j].to;
        if(!flag[too]){
            flag[i]=0;
            break;
            }
        }
   }

正确删点方式:

   for(int i=1;i<=n;i++)
   {
        if(flag[i]&&i!=t)
        for(int j=head[i];j;j=edge[j].next){
        int too=edge[j].to;
        if(!flag[too]){
            judge[i]=1;
            break;
            }
        }
   }
   for(int i=1;i<=n;i++){
       if(judge[i])
       flag[i]=0;
   }

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long int
using namespace std;
int judge[10008];
int n,m,num,cnt,q[50005],pd[10005],head[10005],HEAD[10005],NUM,s,t,flag[10005],ans[10005];
struct Edge{
    int next,dis,to;
}edge[200005];
struct EDGE{
    int next,dis,to;
}road[200005];
void add(int u,int v)
{
    edge[++num].next=head[u];
    edge[num].dis=1;
    edge[num].to=v;
    head[u]=num;
}
void ADD(int u,int v){
    road[++NUM].next=HEAD[u];
    road[NUM].dis=1;
    road[NUM].to=v;
    HEAD[u]=NUM;
}
void dfs(int pos)
{
    for(int i=HEAD[pos];i;i=road[i].next){
        int too=road[i].to;
        if(!flag[too]&&too!=pos){
            flag[too]=1;
            dfs(too);
        }
    }
}
void spfa()
{
    ans[s]=0;
    q[1]=s;
    int l=0,r=1;
    while(l<r)
    {
        l++;
        int now=q[l];
        pd[now]=0;
        for(int i=head[now];i;i=edge[i].next){
            int too=edge[i].to;
            if(!flag[too])continue;
            if(ans[too]>ans[now]+edge[i].dis){
            ans[too]=ans[now]+edge[i].dis;
            if(!pd[too]){
                r++;
                pd[too]=1;
                q[r]=too;
            }
            }
        }
    }
}
int main()
{
    memset(flag,0,sizeof(flag));
    memset(pd,0,sizeof(pd));
    memset(ans,0x3f3f3f3f,sizeof(ans));
   scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
       int u,v;
       scanf("%d%d",&u,&v);
       add(u,v);
       ADD(v,u);
   }
   scanf("%d%d",&s,&t);
   flag[t]=1;
   dfs(t);
   if(!flag[s]){
       printf("-1");
       return 0;
   }
   for(int i=head[s];i;i=edge[i].next)
   {
       if(!flag[edge[i].to]){
           printf("-1");
           return 0;
       }
   }
   for(int i=1;i<=n;i++)
   {
        if(flag[i]&&i!=t)
        for(int j=head[i];j;j=edge[j].next){
        int too=edge[j].to;
        if(!flag[too]){
            judge[i]=1;
            break;
            }
        }
   }
   for(int i=1;i<=n;i++){
       if(judge[i])
       flag[i]=0;
   }
   spfa();
   if(ans[t]==0x3f3f3f3f)
   printf("-1");
   else
   printf("%d",ans[t]);
   return 0;
}

我的代码还是比较长的,网上还有一些BFS+DFS的做法代码贼短...

猜你喜欢

转载自www.cnblogs.com/sky-zxz/p/9390527.html