今天敲了三道图论的题。一道是图的dfs遍历,一道是Bellman——ford,还有一道是最短路。
T1:
图一表示一次街道赛跑的跑道。可以看出有一些路口(用 0 到 N 的整数标号),和连接这些路口的箭头。路口 0 是跑道的起点,路口 N 是跑道的终点。箭头表示单行道。运动员们可以顺着街道从一个路口移动到另一个路口(只能按照箭头所指的方向)。当运动员处于路口位置时,他可以选择任意一条由这个路口引出的街道。
图一:有 10 个路口的街道
一个良好的跑道具有如下几个特点:
每一个路口都可以由起点到达。
从任意一个路口都可以到达终点。
终点不通往任何路口。
运动员不必经过所有的路口来完成比赛。有些路口却是选择任意一条路线都必须到达的(称为“不可避免”的)。在上面的例子中,这些路口是 0,3,6,9。对于给出的良好的跑道,你的程序要确定“不可避免”的路口的集合,不包括起点和终点。
假设比赛要分两天进行。为了达到这个目的,原来的跑道必须分为两个跑道,每天使用一个跑道。第一天,起点为路口 0,终点为一个“中间路口”;第二天,起点是那个中间路口,而终点为路口 N。对于给出的良好的跑道,你的程序要确定“中间路口”的集合。如果良好的跑道 C 可以被路口 S 分成两部分,这两部分都是良好的,并且 S 不同于起点也不同于终点,同时被分割的两个部分满足下列条件:(1)它们之间没有共同的街道(2)S 为它们唯一的公共点,并且 S 作为其中一个的终点和另外一个的起点。那么我们称 S 为“中间路口 ”。在例子中只有路口 3 是中间路口(允许中间路口可以到达本身)。
输入格式:
输入文件包括一个良好的跑道,最多有 50 个路口,100 条单行道。一共有 N+2 行,前面 N+1 行中第 i 行表示以 i 为起点的街道,每个数字表示一个终点。行末用 -2 作为结束。最后一行只有一个数字 -1。
输出格式:
你的程序要有两行输出:
第一行包括:跑道中“不可避免的”路口的数量,接着是这些路口的序号,序号按照升序排列。
第二行包括:跑道中“中间路口”的数量,接着是这些路口的序号,序号按照升序排列。
输入样例:
1 2 -2
3 -2
3 -2
5 4 -2
6 4 -2
6 -2
7 8 -2
9 -2
5 9 -2
-2
-1
输出样例:
2 3 6
1 3
这道题也是我想的时间最多的题了。
首先这道题有两问,第一问:如果去掉这个点以后图不再保持联通,则表明符合第一问的结果。第二问:很明显第二问的答案是从第一问中产生的,符合第二问中的点,要满足以下要求1)这个点可以作为第一天的终点同时也是作为第二天的起点。2)由这个点分开的两天的点之间不能有任何的路径。
先说第一问,刚开始想那不是就把所有点枚举一边,把点去掉,然后判断连通性。刚开始先到判断连通性的时候居然想用dijkstra智障的我以为所有求最短路的算法都可以判断图的连通性,然后才知道传递闭包是floyed的特殊作用。最后决定还是用dfs判断吧,毕竟这个用floyed的话时间复杂度是n^4(>_<,好恐怖)。然后第一问就非常简单啦(只要你不是跟我一样,我把点去掉的方法是在矩阵中把所有和这点有联系的边都去掉,dfs完了以后再改回来,为什么我这么智障),去点的话就直接把dfs中要用到的vis数组中的要去掉的点标记为1就行了。
然后说第二问,第二问中要把所有第一问的点遍历一遍,遍历完以后从起点出发,dfs一边,把所有能够到达的点标记为1,当然,其他的为0。这样的话标记为1得点就是第一天能够到达的点,为0的点就是第二天要到达的点,然后二重循环,如果第一天的点于第二天的点之间有路径就证明这个点是不符合第二问的要求的。最后要特判一下,就是第一天的点可以到达你选的这个点不能到达第一天的点。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<ctime> using namespace std; struct hehe { int v; bool b; }; hehe a[60][60]; int ans1[60],q1=0; int ans2[60],q2=0; int vis[60]; int c1[60],c2[60],q_c1=0,q_c2=0; bool flag=0; int num=0; void init() { int c; while(cin>>c) { if(c==-2) num++; else if(c==-1) break; else a[num][c].v=1; } num-=1; for(int i=1;i<=num;i++) a[i][i].v=0; } void dfs(int k) { if(k==num){flag=1;return;} if(flag) return; for(int i=0;i<=num;i++) { if(a[k][i].v && !vis[i]){vis[i]=1;dfs(i);} } } int main() { //freopen("add.in","r",stdin); //freopen("add.out","w",stdout); memset(a,0,sizeof(a)); init(); /*for(int i=0;i<=num;i++) { for(int j=0;j<=num;j++) cout<<a[i][j].v<<' '; cout<<endl; }*/ for(int i=1;i<num;i++) { /*for(int j=0;j<=num;j++) { if(a[i][j].v==1) { a[i][j].v=0; a[i][j].b=1; } if(a[j][i].v==1) { a[j][i].v=0; a[j][i].b=1; } }*/ flag=0; memset(vis,0,sizeof(vis)); vis[i]=1; vis[0]=1; dfs(0); if(!flag) ans1[++q1]=i; /*for(int j=0;j<=num;j++) { if(a[i][j].b){a[i][j].v=1;a[i][j].b=0;} if(a[j][i].b){a[j][i].v=1;a[i][j].b=0;} }*/ /*for(int i=0;i<=num;i++) { for(int j=0;j<=num;j++) cout<<a[i][j].v<<' '; cout<<endl; } cout<<endl;*/ } flag=0; cout<<q1<<' '; for(int i=1;i<=q1;i++) { cout<<ans1[i]<<' '; } cout<<endl; /*for(int i=0;i<=num;i++) { for(int j=0;j<=num;j++) cout<<a[i][j].v<<' '; cout<<endl; }*/ for(int i=1;i<=q1;i++) { memset(vis,0,sizeof(vis)); vis[ans1[i]]=1; vis[0]=1; dfs(0); //for(int j=0;j<=num;j++) cout<<vis[j]<<' '; cout<<endl; for(int j=0;j<=num;j++) { if(j!=ans1[i]) { if(vis[j]) c1[++q_c1]=j; else c2[++q_c2]=j; } } flag=0; for(int j=1;j<=q_c1;j++) { for(int k=1;k<=q_c2;k++) { if(a[c1[j]][c2[k]].v || a[c2[k]][c1[j]].v) {flag=1; break;} } if(flag) break; } for(int j=1;j<=q_c1;j++) { if(a[ans1[i]][c1[j]].v) {flag=1;break;} } if(!flag) ans2[++q2]=ans1[i]; q_c1=0; q_c2=0; flag=0; } cout<<q2<<' '; for(int i=1;i<=q2;i++) cout<<ans2[i]<<' '; cout<<endl; //fclose(stdin);fclose(stdout); return 0; }
T2:
在一个神秘岛上,有N(1 <= N <= 500)个洞口,标号1..N,它们之间有M (1 <= M <= 2500) 条通道相连。神秘的竟然另外还有W (1 <= W <=200)条传说中的时间虫洞----当到达通道的另一端洞口时,竟然可以比进入的时间要早!
你当然想进行这样的时间之旅,希望从一个洞口s出发,经过几个通道,在比出发早些时候的时间回到洞口s。也许还能碰到自己呢,hehe
根据给定的地图,请判断能否实现这样的愿望。
输入格式:
第一行:一个整数 F (1 <= F <= 5),表示共有F组数据。(多组数据测试)
每组数据:
第1行:三个整数 N M W
第2至M+1行:每行三个整数 (S, E, T),表示在S与E洞口之间有一个双向通道,通过需要T(0 <= T <= 10,000) 秒。
第M+2至M+W+1行:每行三个整数 (S, E, T),表示在S与E洞口之间有一个单向通道,从S到E可以回到之前T(0 <= T <= 10,000) 秒。
输出格式:
共1..F行,每行对应一组数据,如果可以实现愿望输出"YES",否则输出"NO".
输入样例:
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
输出样例:
NO
YES
其实这道题真的没有什么好说的,一遍过的题,就是bellman_ford,最基础的,其他的什么都没有用。直接过了吧(希望万恶的教师某不要说我)
#include<iostream> #include<cstdio> #include<algorithm> #include<ctime> #include<cmath> #include<cstring> #include<utility> using namespace std; struct hehe { int x,y; int v; }; hehe e[6000]; int len=0; int ff,xx,yy,vv; int n,m,k; int dis[6000]; bool Bellman_ford() { bool rel=0; for(int i=1;i<=n;i++) { rel=0; for(int j=1;j<=len;j++) { if(dis[e[j].x]+e[j].v<dis[e[j].y]) { dis[e[j].y]=dis[e[j].x]+e[j].v; rel=1; } } if(!rel) return 0; } return 1; } int main() { // freopen("add.in","r",stdin); //freopen("add.out","w",stdout); cin>>ff; for(int i=1;i<=ff;i++) { len=0; cin>>n>>m>>k; for(int j=1;j<=m;j++) { cin>>xx>>yy>>vv; e[++len].x=xx; e[len].y=yy; e[len].v=vv; e[++len].x=yy; e[len].y=xx; e[len].v=vv; } for(int j=1;j<=k;j++) { cin>>xx>>yy>>vv; e[++len].x=xx; e[len].y=yy; e[len].v=-vv; } if(Bellman_ford()) cout<<"YES"<<endl; else cout<<"NO"<<endl; } //fclose(stdin);fclose(stdout); return 10; }
T3:
Bessie喜欢为在外面的奶牛做晚餐,Bessie按响铃给他们一个信号叫他们进来就可以了。
晚餐将在T (1 <= T <= 1,000,000)毫秒完成,而且Bessie强调那些想吃她晚餐的奶牛必须准时到。
这些牛在F (1 <= F <= 500)各不同的草地标号为1~F用P(1 <= P <= 10,000)个双向的小路连接。Bessie在第1个草地, 给出一头牛走每一条小路所用的时间,问多少个草地上的奶牛可以在T毫秒内到Bessie 所在的草地,假设多头牛可以共享一条路。
输入格式:
第一行:三个整数用空格隔开,T,F,P
第2~P+1 行,每行表示在两个草地间的一条路,给出三个用空格隔开的整数,分别表示,
这条路所连接的一个草地和另一个草地以及奶牛走这条路所需要的毫秒数(1..1,000,000)
输出格式:
一行,可以在T毫秒内到达Bessie所在的位置草地的个数。
输入样例:
1000 5 6
1 2 300
2 4 200
3 4 600
3 4 800
5 3 100
2 5 650
输出样例:
4
万恶的教师某嘛这道题放到这里让练dijkstra。然而数据只有500个点呀!!!这可是简单的来一遍floyed就可以秒过的题呀。还好本宝宝的自制力比较强,没有水过(呵呵)。但是我还是没有用dijkstra来写,而使用了SPFA来写了,也没有什么好说的,我之所以想要用SPFA就是因为这个题有重边的情况,用矩阵的话要判断,懒得要死的我就直接用链表来存了。然后把所有点都求一下到1的最短路就好了。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<ctime> #include<utility> using namespace std; const int maxn=10000; struct hehe { int y,v; int next; }; hehe e[1000000]; int lin[1000000]; int len=0; int t,n,m; int dis[1000000]; bool vis[1000000]; int ans[1000000],q_ans=0; int q[1000000]; int num=0; inline void insert(int xx,int yy,int zz) { e[++len].next=lin[xx]; e[len].y=yy; e[len].v=zz; lin[xx]=len; } void SPFA(int k) { memset(dis,10,sizeof(dis)); memset(vis,0,sizeof(vis)); int head=0,tail=0; q[++tail]=k; vis[k]=1; dis[k]=0; while(head<tail) { int tn=q[++head]; vis[tn]=0; for(int i=lin[tn];i;i=e[i].next) { int w=e[i].y; if(dis[tn]+e[i].v<dis[w]) { dis[w]=dis[tn]+e[i].v; if(!vis[w]) { q[++tail]=w; vis[w]=1; } } } } } int main() { //freopen("add.in","r",stdin); //freopen("add.out","w",stdout); cin>>t>>n>>m; for(int i=1;i<=m;i++) { int xx,yy,zz; cin>>xx>>yy>>zz; insert(xx,yy,zz); insert(yy,xx,zz); } for(int i=2;i<=n;i++) { SPFA(i); ans[i]=dis[1]; } for(int i=2;i<=n;i++) if(ans[i]<=t) num++; cout<<num+1<<endl; //fclose(stdin);fclose(stdout); return 0; }