bzoj 1715: [Usaco2006 Dec]Wormholes 虫洞

这篇博客讨论了如何解决USACO2006 December Gold题目Wormholes(虫洞),涉及到寻找农场中利用虫洞回到过去的路径。通过输入农场数量、地的数量、小路和虫洞的数量,以及描述这些元素的详细信息,判断是否可能回到过去。作者分享了使用DFS(深度优先搜索)而非SPFA(Shortest Path Faster Algorithm)的方法,因为DFS在空间效率上有所提升,并给出了代码实现。

Description

John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地,并有W个虫洞。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。

Input

  • Line 1: 一个整数 F, 表示农场个数。
  • Line 1 of each farm: 三个整数 N, M, W。
  • Lines 2..M+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条用时T秒的小路。
  • Lines M+2..M+W+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条可以使John到达T秒前的虫洞。

Output

  • Lines 1..F: 如果John能在这个农场实现他的目标,输出”YES”,否则输出”NO”。

Sample Input

2

3 3 1

1 2 2

1 3 4

2 3 1

3 1 3

3 2 1

1 2 3

2 3 4

3 1 8

Sample Output

NO

YES

想说的话

这是我第二次写spfa 果断放弃了正常推队列的写法
写成了dfs
一开始空间巨大 140ms
然后空间缩小 28ms
加上fread之后8ms霸榜

这个题想回到过去 所以我们需要的就是判断负环
用前向星存图

struct edge
{
  int to,len;
}e[M];
int head[N],dis[N],nxxt[M];
inline void addedge(int u,int v,int w)
{
  tot++;
  e[tot].to=v;
  nxxt[tot]=head[u];
  e[tot].len=w;
  head[u]=tot;
}
inline void superadd(int u,int v,int w)
{
  addedge(u,v,w);
  addedge(v,u,w);
}

接下来跑spfa 用dfs思想
如果某个点出现多次 那么就出现了负环

inline void spfa(int u)
{
  if(!flag) return;
  vis[u]=true;
  for(int i=head[u];i;i=nxxt[i])
  {
    if(!flag) return;
    int y=e[i].to;
    if(dis[y]>dis[u]+e[i].len)
    {
      dis[y]=dis[u]+e[i].len;
      if(vis[y])
      {
        flag=false;
        return;
      }
      else spfa(y);
    }
  }
  vis[u]=false;
}

敲黑板
以下是重点
由于多组数据 我们需要claen

inline void clean()
{
  tot=0;
  flag=true;
  memset(vis,false,sizeof vis);
  memset(dis,0,sizeof dis);
  memset(nxxt,0,sizeof nxxt);
  memset(head,0,sizeof head);
}

正解代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 100010
#define M 1000010
struct edge
{
  int to,len;
}e[M];
int head[N],dis[N],nxxt[M];
bool vis[N],flag;
int tot;
inline void addedge(int u,int v,int w)
{
  tot++;
  e[tot].to=v;
  nxxt[tot]=head[u];
  e[tot].len=w;
  head[u]=tot;
}
inline void superadd(int u,int v,int w)
{
  addedge(u,v,w);
  addedge(v,u,w);
}
inline void clean()
{
  tot=0;
  flag=true;
  memset(vis,false,sizeof vis);
  memset(dis,0,sizeof dis);
  memset(nxxt,0,sizeof nxxt);
  memset(head,0,sizeof head);
}
inline void spfa(int u)
{
  if(!flag) return;
  vis[u]=true;
  for(int i=head[u];i;i=nxxt[i])
  {
    if(!flag) return;
    int y=e[i].to;
    if(dis[y]>dis[u]+e[i].len)
    {
      dis[y]=dis[u]+e[i].len;
      if(vis[y])
      {
        flag=false;
        return;
      }
      else spfa(y);
    }
  }
  vis[u]=false;
}
int main()
{
  int t;
  scanf("%d",&t);
  while(t--)
  {
    clean();
    int n,m,w;
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1;i<=m;i++)
    {
      int begi,endd,leng;
      scanf("%d%d%d",&begi,&endd,&leng);
      superadd(begi,endd,leng);
    }
    for(int i=1;i<=w;i++)
    {
      int begi,endd,leng;
      scanf("%d%d%d",&begi,&endd,&leng);
      addedge(begi,endd,-leng);
    }
    for(int i=1;i<=n;i++)
    {
      if(!flag) break;
      spfa(i);
    }
    if(flag) puts("NO");
    else puts("YES");
  }
}

写在最后

bfs找负环确实没有dfs快

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值