poj2240
与poj1860相似,我建议这两题好好研究,其中透露很多最短路的性质,不要看他人博客写的很简单,就是bellman-ford什么的,然后一想对就坐上去了,那就失去刷题的本意,刷题本意是更深层次的理解一个算法并且灵活应用,看过大多人都是用最长路的方式求解的,但是最长路能用的本质是因为此题可以用最短路来求解,只是多了一个负号,那么去掉负号最短路就变成最长路了,请不要只是停留在知晓有一种最长路求法这样的表面,应该更深入探讨其本质。(纯属个人意见,大牛指点)
View Code
1 #include <stdio.h>
2 #include <string.h>
3 #include <map>
4 #include <string>
5 #define MAXN 50
6 #define MIN(a,b) ((a)<(b)?(a):(b))
7 using std::map;
8 using std::string;
9 int n,num;
10 map <string, int> m;
11 double g[MAXN][MAXN];
12 double dist[MAXN];
13 void bellman()
14 {
15 for(int i=0;i<=n;dist[i++]=0);
16 dist[0]=-1;
17 for(int k=0;k<n;++k)
18 for(int i=0;i<=n;++i)
19 for(int j=0;j<=n;++j)
20 dist[j]=MIN(dist[j],dist[i]*g[i][j]);
21 }
22 int main()
23 {
24 for(int p=1;scanf("%d",&n),n;++p)
25 {
26 char s[MAXN];
27 for(int i=1;i<=n;++i)
28 {
29 scanf("%s",s);
30 m[s]=i;
31 }
32 double a;
33 char s1[MAXN];
34 memset(g,0,sizeof(g));
35 for(int i=1;i<=n;g[0][i++]=1);
36 scanf("%d",&num);
37 for(int i=0;i<num;++i)
38 {
39 scanf("%s %lf %s",s,&a,s1);
40 g[m[s]][m[s1]]=a;
41 }
42 bellman();
43 bool f=false;
44 for(int i=1;i<=n;++i)
45 for(int j=1;j<=n;++j)
46 if(g[i][j]&&dist[j]>dist[i]*g[i][j])
47 {
48 f=true;
49 break;
50 }
51 printf((f?"Case %d: Yes\n":"Case %d: No\n"),p);
52 }
53 return 0;
54 }
看了姜碧野的spfa的dfs形式跟容易搜索负权回路试了一下,63ms不快,可能是数据不太适合。
View Code
1 #include <stdio.h>
2 #include <string.h>
3 #include <map>
4 #include <string>
5 #define MAXN 50
6 #define INF (1<<29)
7 #define MIN(a,b) ((a)<(b)?(a):(b))
8 using std::map;
9 using std::string;
10 int n,num;
11 map <string, int> m;
12 double g[MAXN][MAXN];
13 double dist[MAXN];
14 bool stack[MAXN];
15 bool f;
16 void SPFA(int a)
17 {
18 stack[a]=true;
19 for(int i=0;i<=n;++i)
20 if(g[a][i]&&dist[i]>dist[a]*g[a][i])
21 {
22 dist[i]=dist[a]*g[a][i];
23 if(!f&&!stack[i])
24 SPFA(i);
25 else
26 {
27 f=true;
28 return ;
29 }
30 }
31 stack[a]=false;
32 }
33 int main()
34 {
35 for(int p=1;scanf("%d",&n),n;++p)
36 {
37 char s[MAXN];
38 for(int i=1;i<=n;++i)
39 {
40 scanf("%s",s);
41 m[s]=i;
42 }
43 double a;
44 char s1[MAXN];
45 memset(g,0,sizeof(g));
46 for(int i=1;i<=n;++i) g[0][i]=1,dist[i]=INF;
47 scanf("%d",&num);
48 for(int i=0;i<num;++i)
49 {
50 scanf("%s %lf %s",s,&a,s1);
51 g[m[s]][m[s1]]=a;
52 }
53 f=false;
54 memset(stack,false,sizeof(stack));
55 dist[0]=-1;
56 for(int i=0;i<=n;++i)
57 {
58 SPFA(i);
59 if(f) break;
60 }
61 printf((f?"Case %d: Yes\n":"Case %d: No\n"),p);
62 }
63 return 0;
64 }
还有必须提一句bellman-ford的负权回路检查只能查出原点可以到达的,像此题给出如下数据:
5
1 2 3 4 5
5
1 0.3 2
2 0.2 3
3 0.1 1
4 4 5
5 5 4
那么大多数直接用bellman-ford的会错误,所以我采用增加一个原点连接所有点,求该原点的最短路,然后负权检查时不检查于该点相连的边,因为词典使我们加的是不存在的,也就是说与这个点有关的回路也是不存在的。
还有一种解法是用floyd,检查方法见dijkstra,bellman-ford,floyd分析比较。
View Code
1 #include <stdio.h>
2 #include <map>
3 #include <string>
4 using std::map;
5 using std::string;
6 #define MAXN 101
7 #define INF -(1<<29)
8 #define MAX(a,b) ((a)>(b)?(a):(b))
9 int num,n;
10 map <string, int> m;
11 double g[MAXN][MAXN];
12 void floyd()
13 {
14 for(int k=1;k<=n;++k)
15 for(int i=1;i<=n;++i)
16 for(int j=1;j<=n;++j)
17 g[i][j]=MAX(g[i][j],g[i][k]*g[k][j]);
18 }
19 int main()
20 {
21 for(int p=1;scanf("%d",&num),num;++p)
22 {
23 n=num;
24 char s[MAXN],s1[MAXN];
25 for(int i=1;i<=num;++i)
26 {
27 scanf("%s",s);
28 m[s]=i;
29 }
30 for(int i=1;i<=num;++i)
31 for(int j=1;j<=num;++j)
32 g[i][j]=(i==j?-1:INF);
33 scanf("%d",&num);
34 double a;
35 for(int k=0;k<num;++k)
36 {
37 scanf("%s %lf %s",s,&a,s1);
38 g[m[s]][m[s1]]=a;
39 }
40 floyd();
41 bool key=false;
42 for(int i=1;i<=n;++i)
43 if(g[i][i]>1)
44 {
45 key=true;
46 break;
47 }
48 printf("Case ");
49 printf("%d",p);
50 printf((key?": Yes\n":": No\n"));
51 }
52 return 0;
53 }