Vijos P1391 想越狱的小杉

10 篇文章 0 订阅

Vijos P1391 想越狱的小杉


题目

背景

这次小杉来到了经典美剧《越狱》的场景里……
他被抓起来了(-.-干嘛幻想这么郁闷的场景……)。
小杉身为新一代的Scofield,在挖了半个月之后终于挖通牢房里的地道。
在地道里,无数的管道路线困惑了他。
(若对情节有任何疑问,请观看原剧)
描述

小杉看了看自己的纹身,明白了整个管道网是由N个小房间和若干小房间之间的单向的管道组成的。
小房间编号为不超过N的正整数。
对于某个管道,小杉只能在人品不超过一定程度时通过。
小杉一开始在房间1,现在小杉想知道,每个小房间他最多能够以人品多少的状态到达。
注意,小杉的人品在出发以后是不会改变的。
格式

输入格式

每组测试数据的
第一行有一个正整数N(1<=N<=2000)。
接下来若干行描述管道,每行三个正整数A,B,R(1<=A,B<=N),表示A房间有一条到达B房间的管道,且小杉的人品不超过R时可以通过(注意从B房间不可由此管道到达A房间,即管道是单向的)
整个输入数据以一行0 0 0结束
特别地,对于30%的数据,有N<=100
输出格式

对每组测试数据输出N-1行,分别表示对于2到N号的小房间,小杉最多能够以人品多少的状态到达。
样例1

样例输入1

4
1 2 30
1 3 20
2 3 25
3 4 30
2 4 20
0 0 0

样例输出1

30
25
25

限制

每个测试点1s
提示

对于样例数据:
小杉最多能够在人品为30的情况下到达小房间2(1->2)
小杉最多能够在人品为25的情况下到达小房间3(1->2->3)
小杉最多能够在人品为25的情况下到达小房间4(1->2->3->4)


题解

方法一:

取巧的方法,成功地把数据被氵过去了

二分查找答案+BFS判断是否能到终点,时间复杂度N*M*log(maxn)(正解还没有看,先氵一波)

方法二:

(应该是正解吧)

先按边的大小排序,然后不断地把大边往图里建,然后每次从这条大边做一次 spfa s p f a 修改答案

显然,这里的时间复杂度远小于 NM N ∗ M (反正我不会算,就瞎写一个吧)

方法一的AC情况:
这里写图片描述
方法二的AC情况:
这里写图片描述


代码

氵题的代码

#include<cstdio>
#include<cstring>
using namespace std;

int n,tot,maxn;
int lnk[2005],q[2005],ans[2005];
bool vis[2005];
struct edge
{
    int nxt,y,v;
} e[4000005];

int readln()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}

void add(int x,int y,int v)
{
    tot++;e[tot].nxt=lnk[x];lnk[x]=tot;e[tot].y=y;e[tot].v=v;
}

int max(int x,int y){return x>y?x:y;}

bool bfs(int limit,int goal)
{
    memset(vis,false,sizeof(vis));
    int head=0,tail=1;
    q[1]=1;vis[1]=true;
    while (head<tail)
    {
        int x=q[++head];
        for (int i=lnk[x];i;i=e[i].nxt)
        {
            int y=e[i].y,v=e[i].v;
            if (vis[y]==false&&v>=limit) {
                if (y==goal) return true;
                q[++tail]=y;vis[y]=true;
            }
        }
    }
    return false;
}

int main()
{
    n=readln();
    int x,y,z=1;
    while (true) {
        x=readln();y=readln();z=readln();
        if (x+y+z==0) break;
        add(x,y,z);maxn=max(maxn,z);
    }
    for (int i=2;i<=n;i++)
    {
        int l=1,r=maxn,mid;
        while (l<=r)
        {
            mid=(l+r)>>1;
            if (bfs(mid,i)) l=mid+1,ans[i]=mid; else r=mid-1;
        }
    }
    for (int i=2;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

正解

#include<cstdio>
#include<cstdlib>
#define maxn 2005
using namespace std;

int n,tot,cnt;
int lnk[maxn],q[maxn],ans[maxn],a[maxn*maxn],b[maxn*maxn],c[maxn*maxn];
struct edge
{
    int nxt,v,y;
} e[maxn*maxn];

int readln()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}

void add(int x,int y,int v)
{
    cnt++;e[cnt].nxt=lnk[x];lnk[x]=cnt;e[cnt].y=y;e[cnt].v=v;
}

int min(int x,int y){return x<y?x:y;}

bool spfa(int x)
{
    int head=0,tail=1;
    q[1]=x;
    while (head<tail)
    {
        int x=q[++head];
        for (int i=lnk[x];i;i=e[i].nxt)
        {
            int y=e[i].y,v=min(e[i].v,ans[x]);
            if (v>ans[y])
            {
                ans[y]=v;q[++tail]=y;
            }
        }
    }
    return false;
}

void qsort(int l,int r)
{
    int i=l,j=r,mid=c[rand()%(r-l+1)+l],t;
    do {
        while (c[i]>mid) i++;
        while (c[j]<mid) j--;
        if (i<=j) {
            t=a[i];a[i]=a[j];a[j]=t;
            t=b[i];b[i]=b[j];b[j]=t;
            t=c[i];c[i]=c[j];c[j]=t;
            i++;j--;
        }
    } while (i<=j);
    if (i<r) qsort(i,r);
    if (l<j) qsort(l,j);
}

int main()
{
    n=readln();
    while (true) {
        int x,y,z;
        x=readln();y=readln();z=readln();
        if (x+y+z==0) break;
        a[++tot]=x;b[tot]=y;c[tot]=z;
    }
    qsort(1,tot);
    ans[1]=0x7fffffff;
    for (int i=1;i<=tot;i++)
    {
        add(a[i],b[i],c[i]);
        spfa(a[i]);
    }
    for (int i=2;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值