2016年 团体程序设计天梯赛 - 初赛

此处有目录↑

3h内只AC了L1-1 ~ L1-8、L2-1 ~ L2-3、L3-2

L3-1就快写完了,还是手速太慢  【已补完】

L2-4(小根堆忘了如何写了...)【已补完】和L3-3没时间看了

比赛网址:https://www.patest.cn/contests/2016gplt-1

交题网址: https://www.patest.cn/contests/gplt


L1-1. 到底有多二 (模拟)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

一个整数“犯二的程度”定义为该数字中包含2的个数与其位数的比值。如果这个数是负数,则程度增加0.5倍;如果还是个偶数,则再增加1倍。例如数字“-13142223336”是个11位数,其中有3个2,并且是负数,也是偶数,则它的犯二程度计算为:3/11*1.5*2*100%,约为81.82%。本题就请你计算一个给定整数到底有多二。

输入格式:

输入第一行给出一个不超过50位的整数N。

输出格式:

在一行中输出N犯二的程度,保留小数点后两位。

输入样例:
-13142223336
输出样例:
81.82%
按照题目要求统计2的个数,并判断是否为负数,是否为偶数即可

#include 
#include 
#include 

using namespace std;

int len;
char s[55];
double ans,num1,num2;

int main() {
    while(1==scanf("%s",s)) {
        num1=num2=1;
        len=strlen(s);
        ans=0;
        for(int i=0;i

L1-2. 大笨钟 (模拟)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

微博上有个自称“大笨钟V”的家伙,每天敲钟催促码农们爱惜身体早点睡觉。不过由于笨钟自己作息也不是很规律,所以敲钟并不定时。一般敲钟的点数是根据敲钟时间而定的,如果正好在某个整点敲,那么“当”数就等于那个整点数;如果过了整点,就敲下一个整点数。另外,虽然一天有24小时,钟却是只在后半天敲1~12下。例如在23:00敲钟,就是“当当当当当当当当当当当”,而到了23:01就会是“当当当当当当当当当当当当”。在午夜00:00到中午12:00期间(端点时间包括在内),笨钟是不敲的。

下面就请你写个程序,根据当前时间替大笨钟敲钟。

输入格式:

输入第一行按照“hh:mm”的格式给出当前时间。其中hh是小时,在00到23之间;mm是分钟,在00到59之间。

输出格式:

根据当前时间替大笨钟敲钟,即在一行中输出相应数量个“Dang”。如果不是敲钟期,则输出:

Only hh:mm.  Too early to Dang.

其中“hh:mm”是输入的时间。

输入样例1:
19:05
输出样例1:
DangDangDangDangDangDangDangDang
输入样例2:
07:05
输出样例2:
Only 07:05.  Too early to Dang.

按照题目要求判断即可

若hh<12||(hh==12&&mm==0),则不敲钟,注意需要格式输出

否则,若mm==0,输出hh-12次Dang;若mm!=0,输出hh-11次Dang

#include 
#include 
#include 

using namespace std;

int h,m;

int main() {
    while(2==scanf("%d:%d",&h,&m)) {
        if(h<12||(h==12&&m==0)) {
            printf("Only %02d:%02d.  Too early to Dang.\n",h,m);
        }
        else {
            h-=12;
            if(m!=0) {
                ++h;
            }
            while(h-->0) {
                printf("Dang");
            }
            printf("\n");
        }
    }
    return 0;
}

L1-3. 谁先倒

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和,谁就输了,输家罚一杯酒。两人同赢或两人同输则继续下一轮,直到唯一的赢家出现。

下面给出甲、乙两人的酒量(最多能喝多少杯不倒)和划拳记录,请你判断两个人谁先倒。

输入格式:

输入第一行先后给出甲、乙两人的酒量(不超过100的非负整数),以空格分隔。下一行给出一个正整数N(<=100),随后N行,每行给出一轮划拳的记录,格式为:

甲喊 甲划 乙喊 乙划

其中“喊”是喊出的数字,“划”是划出的数字,均为不超过100的正整数(两只手一起划)。

输出格式:

在第一行中输出先倒下的那个人:A代表甲,B代表乙。第二行中输出没倒的那个人喝了多少杯。题目保证有一个人倒下。注意程序处理到有人倒下就终止,后面的数据不必处理。

输入样例:
1 1
6
8 10 9 12
5 10 5 10
3 8 5 12
12 18 1 13
4 16 12 15
15 1 1 16
输出样例:
A
1

直接判断每次输赢即可,若有人倒下,则读入的数据不再判断输赢,需要注意必须是一人赢,另一人输,才会有人喝酒


#include 
#include 
#include 

using namespace std;

int loseeA,loseeB,n;
int pA,sA,pB,sB,cntA,cntB,cups;
char lose;

int main() {
    while(2==scanf("%d%d",&loseeA,&loseeB)) {
        lose='\0';
        cntA=cntB=0;
        scanf("%d",&n);
        while(n-->0) {
            scanf("%d%d%d%d",&pA,&sA,&pB,&sB);
            if(lose=='\0') {
                if(sA==pA+pB&&sB!=pA+pB) {
                    ++cntA;
                    if(cntA>loseeA) {
                        lose='A';
                        cups=cntB;
                    }
                }
                else if(sA!=pA+pB&&sB==pA+pB) {
                    ++cntB;
                    if(cntB>loseeB) {
                        lose='B';
                        cups=cntA;
                    }
                }
            }
        }
        printf("%c\n%d\n",lose,cups);
    }
    return 0;
}


L1-4. 帅到没朋友 (标记)

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

当芸芸众生忙着在朋友圈中发照片的时候,总有一些人因为太帅而没有朋友。本题就要求你找出那些帅到没有朋友的人。

输入格式:

输入第一行给出一个正整数N(<=100),是已知朋友圈的个数;随后N行,每行首先给出一个正整数K(<=1000),为朋友圈中的人数,然后列出一个朋友圈内的所有人——为方便起见,每人对应一个ID号,为5位数字(从00000到99999),ID间以空格分隔;之后给出一个正整数M(<=10000),为待查询的人数;随后一行中列出M个待查询的ID,以空格分隔。

注意:没有朋友的人可以是根本没安装“朋友圈”,也可以是只有自己一个人在朋友圈的人。虽然有个别自恋狂会自己把自己反复加进朋友圈,但题目保证所有K超过1的朋友圈里都至少有2个不同的人。

输出格式:

按输入的顺序输出那些帅到没朋友的人。ID间用1个空格分隔,行的首尾不得有多余空格。如果没有人太帅,则输出“No one is handsome”。

注意:同一个人可以被查询多次,但只输出一次。

输入样例1:
3
3 11111 22222 55555
2 33333 44444
4 55555 66666 99999 77777
8
55555 44444 10000 88888 22222 11111 23333 88888
输出样例1:
10000 88888 23333
输入样例2:
3
3 11111 22222 55555
2 33333 44444
4 55555 66666 99999 77777
4
55555 44444 22222 11111
输出样例2:
No one is handsome

直接用bool数组标记某个人是否处于一个朋友圈中即可,只需要注意朋友圈只有一人时不标记

查询时,再用bool数组标记该人是否被查询过,没有查询过就存入数组,最后按照总人数格式化输出即可

注意:输出人时需要输出5位数字,即包含前导0,查了好久才发现,要不然就有时间了

#include 
#include 
#include 

using namespace std;

int n,num,people;
int ans[10005],tot;
bool notHandsome[100005];
bool vis[100005];

int main() {
    while(1==scanf("%d",&n)) {
        memset(notHandsome,false,sizeof(notHandsome));
        memset(vis,false,sizeof(vis));
        while(n-->0) {
            scanf("%d",&num);
            for(int i=0;i0) {
            scanf("%d",&people);
            if(!vis[people]) {
                vis[people]=true;
                if(!notHandsome[people]) {
                    ans[tot++]=people;
                }
            }
        }
        if(tot==0) {
            printf("No one is handsome\n");
        }
        else {
            printf("%05d",ans[0]);
            for(int i=1;i


L1-5. 重要的话说三遍 (模拟)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

这道超级简单的题目没有任何输入。

你只需要把这句很重要的话 —— “I'm gonna WIN!”——连续输出三遍就可以了。

注意每遍占一行,除了每行的回车不能有任何多余字符。





按照题目要求输出即可

#include 
#include 
#include 

using namespace std;

int main() {
    printf("I'm gonna WIN!\n");
    printf("I'm gonna WIN!\n");
    printf("I'm gonna WIN!\n");
    return 0;
}

L1-6. 奇偶分家 (模拟)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

给定N个正整数,请统计奇数和偶数各有多少个?

输入格式:

输入第一行给出一个正整N(<= 1000);第2行给出N个正整数,以空格分隔。

输出格式:

在一行中先后输出奇数的个数、偶数的个数。中间以1个空格分隔。

输入样例:
9
88 74 101 26 15 0 34 22 77
输出样例:
3 6
按照题目要求统计奇数和偶数的个数即可

#include 
#include 
#include 

using namespace std;

int n,a,no,ne;

int main() {
    while(1==scanf("%d",&n)) {
        no=ne=0;
        while(n-->0) {
            scanf("%d",&a);
            if((a&1)==1) {
                ++no;
            }
            else {
                ++ne;
            }
        }
        printf("%d %d\n",no,ne);
    }
    return 0;
}


L1-7. 输出GPLT (标记)

时间限制
150 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按“GPLTGPLT....”这样的顺序输出,并忽略其它字符。当然,四种字符(不区分大小写)的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按GPLT的顺序打印,直到所有字符都被输出。

输入格式:

输入在一行中给出一个长度不超过10000的、仅由英文字母构成的非空字符串。

输出格式:

在一行中按题目要求输出排序后的字符串。题目保证输出非空。

输入样例:
pcTclnGloRgLrtLhgljkLhGFauPewSKgt
输出样例:
GPLTGPLTGLTGLGLL

统计字符串中各所需字符的出现次数,以及所有所需字符出现的总次数

输出时,若还有能输出的所需字符,则进入循环,按照顺序输出还能输出的所需字符

#include 
#include 
#include 

using namespace std;

char s[10005];
int num[5];

const char ch[5]={"GPLT"};

int main() {
    while(1==scanf("%s",s)) {
        num[0]=num[1]=num[2]=num[3]=0;
        for(int i=0;s[i]!='\0';++i) {
            if(s[i]=='G'||s[i]=='g') {
                ++num[0];
                ++num[4];
            }
            if(s[i]=='P'||s[i]=='p') {
                ++num[1];
                ++num[4];
            }
            if(s[i]=='L'||s[i]=='l') {
                ++num[2];
                ++num[4];
            }
            if(s[i]=='T'||s[i]=='t') {
                ++num[3];
                ++num[4];
            }
        }
        while(num[4]>0) {
            for(int i=0;i<4;++i) {
                if(num[i]>0) {
                    --num[i];
                    --num[4];
                    printf("%c",ch[i]);
                }
            }
        }
        printf("\n");
    }
    return 0;
}

L1-8. 后天 (模拟)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

如果今天是星期三,后天就是星期五;如果今天是星期六,后天就是星期一。我们用数字1到7对应星期一到星期日。给定某一天,请你输出那天的“后天”是星期几。

输入格式:

输入第一行给出一个正整数D(1 <= D <=7),代表星期里的某一天。

输出格式:

在一行中输出D天的后天是星期几。

输入样例:
3
输出样例:
5
用的取模的方法,直接判断输出也可以

#include 
#include 
#include 

using namespace std;

int n;

int main() {
    while(1==scanf("%d",&n)) {
        n=(n+2)%7;
        if(n==0) {
            n=7;
        }
        printf("%d\n",n);
    }
    return 0;
}


L2-1. 抢红包 (模拟)

时间限制
300 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

没有人没抢过红包吧…… 这里给出N个人之间互相发红包、抢红包的记录,请你统计一下他们抢红包的收获。

输入格式:

输入第一行给出一个正整数N(<= 104),即参与发红包和抢红包的总人数,则这些人从1到N编号。随后N行,第i行给出编号为i的人发红包的记录,格式如下:

K N1 P1 ... NK PK

其中K(0 <= K <= 20)是发出去的红包个数,Ni是抢到红包的人的编号,Pi(> 0)是其抢到的红包金额(以分为单位)。注意:对于同一个人发出的红包,每人最多只能抢1次,不能重复抢。

输出格式:

按照收入金额从高到低的递减顺序输出每个人的编号和收入金额(以元为单位,输出小数点后2位)。每个人的信息占一行,两数字间有1个空格。如果收入金额有并列,则按抢到红包的个数递减输出;如果还有并列,则按个人编号递增输出。

输入样例:
10
3 2 22 10 58 8 125
5 1 345 3 211 5 233 7 13 8 101
1 7 8800
2 1 1000 2 1000
2 4 250 10 320
6 5 11 9 22 8 33 7 44 10 55 4 2
1 3 8800
2 1 23 2 123
1 8 250
4 2 121 4 516 7 112 9 10
输出样例:
1 11.63
2 3.63
8 3.63
3 2.11
7 1.69
6 -1.67
9 -2.18
10 -3.26
5 -3.26
4 -12.32

按照题意模拟资金的流入与流出即可,注意发红包的人需要减去其发红包的金额总和

然后按照题目要求排序即可

#include 
#include 
#include 

using namespace std;

struct Node {
    int index,num,money;

    bool operator < (const Node& a) const {
        if(money!=a.money) {
            return money>a.money;
        }
        return num>a.num||(index0) {
                scanf("%d%d",&nn,&pp);
                people[i].money-=pp;
                people[nn].money+=pp;
                ++people[nn].num;
            }
        }
        sort(people+1,people+1+n);
        for(int i=1;i<=n;++i) {
            printf("%d %.2lf\n",people[i].index,0.01*people[i].money);
        }
    }
    return 0;
}


L2-2. 排座位 (并查集)

时间限制
150 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。

输入格式:

输入第一行给出3个正整数:N(<= 100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:“宾客1 宾客2 关系”,其中“关系”为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K行,每行给出一对需要查询的宾客编号。

这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。

输出格式:

对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出“No problem”;如果他们之间并不是朋友,但也不敌对,则输出“OK”;如果他们之间有敌对,然而也有共同的朋友,则输出“OK but...”;如果他们之间只有敌对关系,则输出“No way”。

输入样例:
7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2
输出样例:
No problem
OK
OK but...
No way

用并查集维护朋友关系

用enemy[i][j]表示i和j有没有直接的敌对关系

然后根据查询结果按照题目要求输出不同的信息

#include 
#include 
#include 

using namespace std;

int n,m,k,g1,g2,r,p1,p2;
int par[105];
bool enemy[105][105];

int getPar(int a) {
    if(par[a]!=a) {
        par[a]=getPar(par[a]);
    }
    return par[a];
}

int main() {
    while(3==scanf("%d%d%d",&n,&m,&k)) {
        for(int i=1;i<=n;++i) {
            par[i]=i;
            for(int j=i;j<=n;++j) {
                enemy[i][j]=enemy[j][i]=false;
            }
        }
        while(m-->0) {
            scanf("%d%d%d",&g1,&g2,&r);
            if(r==1) {
                p1=getPar(g1);
                p2=getPar(g2);
                if(p1!=p2) {
                    par[p1]=p2;
                }
            }
            else {
                enemy[g1][g2]=enemy[g2][g1]=true;
            }
        }
        while(k-->0) {
            scanf("%d%d",&g1,&g2);
            if(getPar(g1)==getPar(g2)) {
                printf("%s\n",enemy[g1][g2]?"OK but...":"No problem");
            }
            else {
                printf("%s\n",enemy[g1][g2]?"No way":"OK");
            }
        }
    }
    return 0;
}


L2-3. 玩转二叉树 (分治)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(<=30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:
7
1 2 3 4 5 6 7
4 1 3 2 6 5 7
输出样例:
4 6 1 7 5 3 2

和模拟赛的一样类型的题,代码改一下就能重新建立二叉树

不必做镜像反转,只用在层次遍历的时候,先加入右儿子结点,再加入左儿子结点即可

#include 
#include 
#include 

using namespace std;

const int MAXN=35;

int n,cnt,root;
int inod[MAXN],preod[MAXN];
int q[35],head,tail;

struct Node {
    int lson,rson,num;
}tr[MAXN];


int dfs(int pl,int pr,int il,int ir) {
    if(pl==pr) {
        tr[cnt].lson=tr[cnt].rson=-1;
        tr[cnt].num=preod[pl];
        return cnt++;
    }
    for(int i=il;i<=ir;++i) {
        if(preod[pl]==inod[i]) {
            int cur=cnt++;
            tr[cur].lson=tr[cur].rson=-1;
            tr[cur].num=preod[pl];
            if(il



L2-4. 关于堆的判断 (小根堆)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

将一系列给定数字顺序插入一个初始为空的小顶堆H[]。随后判断一系列相关命题是否为真。命题分下列几种:

  • “x is the root”:x是根结点;
  • “x and y are siblings”:x和y是兄弟结点;
  • “x is the parent of y”:x是y的父结点;
  • “x is a child of y”:x是y的一个子结点。

输入格式:

每组测试第1行包含2个正整数N(<= 1000)和M(<= 20),分别是插入元素的个数、以及需要判断的命题数。下一行给出区间[-10000, 10000]内的N个要被插入一个初始为空的小顶堆的整数。之后M行,每行给出一个命题。题目保证命题中的结点键值都是存在的。

输出格式:

对输入的每个命题,如果其为真,则在一行中输出“T”,否则输出“F”。

输入样例:
5 4
46 23 26 24 10
24 is the root
26 and 23 are siblings
46 is the parent of 23
23 is a child of 10
输出样例:
F
T
F
T
比赛时忘记了小根堆如何写了,下来看了一边堆排序,又回忆起原理即过程,发现这题很简单,因为只有插入操作

本来还怕存在重复的键值,但是这样就无法进行查询,就推测出应该键值唯一,然后用数组标记即可


#include 
#include 
#include 

using namespace std;

const int ORD=10002;

int n,m,a[1005],indx[20005],x,y;
char cmd[25],tmp[25];

void minHeapUp(int i) {
    if(i==1) {
        return ;
    }
    int p=i>>1;
    if(a[i]0) {
            scanf("%d%s",&x,cmd);
            if(cmd[0]=='a') {
                scanf("%d%s%s",&y,cmd,tmp);
                printf("%c\n",(indx[x+ORD]>>1)==(indx[y+ORD]>>1)?'T':'F');
            }
            else {
                scanf("%s%s",tmp,cmd);
                if(cmd[0]=='r') {
                    printf("%c\n",indx[x+ORD]==1?'T':'F');
                }
                else if(cmd[0]=='p') {
                    scanf("%s%d",tmp,&y);
                    printf("%c\n",indx[x+ORD]==(indx[y+ORD]>>1)?'T':'F');
                }
                else {
                    scanf("%s%d",tmp,&y);
                    printf("%c\n",(indx[x+ORD]>>1)==indx[y+ORD]?'T':'F');
                }
            }
        }
    }
    return 0;
}


L3-1. 天梯地图 (最短路 [Dijkstra])

时间限制
300 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。

输入格式:

输入在第一行给出两个正整数N(2 <= N <=500)和M,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M行,每行按如下格式给出一条道路的信息:

V1 V2 one-way length time

其中V1V2是道路的两个端点的编号(从0到N-1);如果该道路是从V1V2的单行线,则one-way为1,否则为0;length是道路的长度;time是通过该路所需要的时间。最后给出一对起点和终点的编号。

输出格式:

首先按下列格式输出最快到达的时间T和用节点编号表示的路线:

Time = T: 起点 => 节点1 => ... => 终点

然后在下一行按下列格式输出最短距离D和用节点编号表示的路线:

Distance = D: 起点 => 节点1 => ... => 终点

如果最快到达路线不唯一,则输出几条最快路线中最短的那条,题目保证这条路线是唯一的。而如果最短距离的路线不唯一,则输出途径节点数最少的那条,题目保证这条路线是唯一的。

如果这两条路线是完全一样的,则按下列格式输出:

Time = T; Distance = D: 起点 => 节点1 => ... => 终点

输入样例1:
10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
5 4 0 2 3
5 9 1 1 4
0 6 0 1 1
7 3 1 1 2
8 3 1 1 2
2 5 0 2 2
2 1 1 1 1
1 5 0 1 3
1 4 0 1 1
9 7 1 1 3
3 1 0 2 5
6 3 1 2 1
5 3
输出样例1:
Time = 6: 5 => 4 => 8 => 3
Distance = 3: 5 => 1 => 3
输入样例2:
7 9
0 4 1 1 1
1 6 1 3 1
2 6 1 1 1
2 5 1 2 2
3 0 0 1 1
3 1 1 3 1
3 2 1 2 1
4 5 0 2 2
6 5 1 2 1
3 5
输出样例2:
Time = 3; Distance = 4: 3 => 2 => 5
由于有两个关键字,所以在普通最短路更新的基础上, 增加一个:若第一关键字相等,但当前第二关键字更小,也更新

然后两个最短路唯一的不同就是:第一关键字和第二关键字不同

处理完最短路后比较路径,按照题目要求输出即可


发现貌似距离最短路更新时,第二关键字(所经点的个数)并不影响,刚开始没有给cnt[e]赋值也AC了,看来还是数据比较水

#include 
#include 
#include 
#include 
#include 

using namespace std;

const int MAXN=505;

struct Node {
    int e,d,t;

    Node(int ee,int dd,int tt):e(ee),d(dd),t(tt) {}

    bool operator < (const Node& a) const {
        if(t!=a.t) {
            return t>a.t;
        }
        return d>a.d;
    }
}cur(0,0,0);

int n,m,s,e,type,t,d,sta,des,tmp;
vector g[MAXN];
priority_queue q;
int tim[MAXN],dis[MAXN],pre[MAXN],cnt[MAXN];
int ans_t[MAXN],num_t,ans_d[MAXN],num_d,Time,Distance;

void Dijkstra_t() {//时间最短的最短路;e代表点,d代表距离,t代表时间
    memset(tim,0x3f,sizeof(tim));
    memset(dis,0x3f,sizeof(dis));
    priority_queue q;
    q.push(Node(sta,0,0));
    tim[sta]=dis[sta]=0;
    while(!q.empty()) {
        cur=q.top();
        q.pop();
        for(int i=g[cur.e].size()-1;i>=0;--i) {
            e=g[cur.e][i].e;
            d=cur.d+g[cur.e][i].d;
            t=cur.t+g[cur.e][i].t;
            if(t q;
    q.push(Node(sta,0,0));
    tim[sta]=dis[sta]=0;
    while(!q.empty()) {
        cur=q.top();
        q.pop();
        for(int i=g[cur.e].size()-1;i>=0;--i) {
            e=g[cur.e][i].e;
            d=cur.d+g[cur.e][i].d;
            t=cur.t+1;
            if(d=0;--i) {
        if(ans_t[i]!=ans_d[i]) {
            return false;
        }
    }
    return true;
}

int main() {
    while(2==scanf("%d%d",&n,&m)) {
        for(int i=0;i0) {
            scanf("%d%d%d%d%d",&s,&e,&type,&d,&t);
            g[s].push_back(Node(e,d,t));
            if(type==0) {
                g[e].push_back(Node(s,d,t));
            }
        }
        scanf("%d%d",&sta,&des);
        Dijkstra_t();
        tmp=des;
        num_t=0;
        while(tmp!=sta) {
            ans_t[num_t++]=tmp;
            tmp=pre[tmp];
        }
        Time=tim[des];

        Dijkstra_d();
        tmp=des;
        num_d=0;
        while(tmp!=sta) {
            ans_d[num_d++]=tmp;
            tmp=pre[tmp];
        }
        Distance=dis[des];

        if(isEqual()) {
            printf("Time = %d; Distance = %d: %d",Time,Distance,sta);
            while(--num_t>=0) {
                printf(" => %d",ans_t[num_t]);
            }
        }
        else {
            printf("Time = %d: %d",Time,sta);
            while(--num_t>=0) {
                printf(" => %d",ans_t[num_t]);
            }
            printf("\nDistance = %d: %d",Distance,sta);
            while(--num_d>=0) {
                printf(" => %d",ans_d[num_d]);
            }
        }
        printf("\n");
    }
    return 0;
}





L3-2. 喊山 (BFS)

时间限制
150 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂—喂喂喂……”的呼唤。呼唤声通过空气的传递,回荡于深谷之间,传送到人们耳中,发出约定俗成的“讯号”,达到声讯传递交流的目的。原来它是彝族先民用来求援呼救的“讯号”,慢慢地人们在生活实践中发现了它的实用价值,便把它作为一种交流工具世代传袭使用。(图文摘自:http://news.xrxxw.com/newsshow-8018.html)

一个山头呼喊的声音可以被临近的山头同时听到。题目假设每个山头最多有两个能听到它的临近山头。给定任意一个发出原始信号的山头,本题请你找出这个信号最远能传达到的地方。

输入格式:

输入第一行给出3个正整数n、m和k,其中n(<=10000)是总的山头数(于是假设每个山头从1到n编号)。接下来的m行,每行给出2个不超过n的正整数,数字间用空格分开,分别代表可以听到彼此的两个山头的编号。这里保证每一对山头只被输入一次,不会有重复的关系输入。最后一行给出k(<=10)个不超过n的正整数,数字间用空格分开,代表需要查询的山头的编号。

输出格式:

依次对于输入中的每个被查询的山头,在一行中输出其发出的呼喊能够连锁传达到的最远的那个山头。注意:被输出的首先必须是被查询的个山头能连锁传到的。若这样的山头不只一个,则输出编号最小的那个。若此山头的呼喊无法传到任何其他山头,则输出0。

输入样例:
7 5 4
1 2
2 3
3 1
4 5
5 6
1 4 5 7
输出样例:
2
6
4
0

可以抽象为题目给了一个边权值为1的无向稀疏图(现在再仔细读题,就是一些链(或者环)么),然后可以直接bfs,找到路最长的点即可

因为本题可能存在一个环,所以不能直接dfs或者用链表模拟,否则可能会误判起始点的相邻点为最远点

例如:

2—4

|    |

1—3

用dfs或者链表模拟,就会误判以1为起点的最远点为2,而正确答案为4

#include 
#include 
#include 
#include 

using namespace std;

const int MAXN=10005;

int n,m,k,s,e,mxdis;
vector g[MAXN];
int q[MAXN],head,tail;
int dis[MAXN];

int main() {
    while(3==scanf("%d%d%d",&n,&m,&k)) {
        for(int i=1;i<=n;++i) {
            g[i].clear();
        }
        while(m-->0) {
            scanf("%d%d",&s,&e);
            g[s].push_back(e);
            g[e].push_back(s);
        }

        while(k-->0) {
            scanf("%d",&s);
            if(g[s].size()==0) {
                printf("0\n");
            }
            else {
                memset(dis,-1,sizeof(dis));
                mxdis=head=tail=0;
                dis[s]=0;
                q[tail++]=s;
                while(head!=tail) {
                    s=q[head++];
                    for(int i=g[s].size()-1;i>=0;--i) {
                        e=g[s][i];
                        if(dis[e]==-1) {
                            dis[e]=dis[s]+1;
                            mxdis=max(mxdis,dis[e]);
                            q[tail++]=e;
                        }
                    }
                }
                for(int i=1;i<=n;++i) {
                    if(dis[i]==mxdis) {
                        printf("%d\n",i);
                        break;
                    }
                }
            }
        }
    }
    return 0;
}




你可能感兴趣的:(搜索,分治,并查集,模拟,最短路)