|
这一题仍然是最短路径,一次AC!好高兴啊。题目是单纯的最短路径的小变形,只要从妈妈说的第二个路口开始找最短路径开始就可以了。
#include <stdio.h> #define DEBUG 1
const int N=1111 ; const int Max=0xfffffff ; int n, sum, map[N][N], dis[N], used[N], mo[30002] ;
void Dijstra( ) { int i, j, index, min ; for( i=1; i<=n; ++i ){ //从第二个路口开始找最短路径,因为,第一个 //路口是必须走的,因为妈妈看着我呢! dis[i] = map[mo[2]][i] ; used[i] = 0 ; } used[1] = 1 ; used[mo[2]] = 1 ; for( i=1; i<=n; ++i ){ min = Max ; for( j=1; j<=n; ++j ){ if( !used[j] && min>dis[j] ){ index = j ; min = dis[j] ; } } used[index] = 1 ; for( j=1; j<=n; ++j ) if( !used[j] && dis[j] > dis[index]+map[index][j] ) dis[j] = dis[index]+map[index][j] ; } //计算差值 printf("Y %d\n", sum-dis[n] ) ; }
int main() { #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.txt","r",stdin); freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.txt","w",stdout); #endif int i, j, k, r, di, flag, x, y, sets, mother ; scanf("%d", &sets ) ; for( k=1; k<=sets; ++k ){ printf("TEST %d ", k ) ; scanf("%d%d", &n, &r ) ; for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ) map[i][j] = Max ; map[i][i] = 0 ; } for( i=1; i<=r; ++i ){ scanf("%d%d%d", &x, &y, &di ) ; map[x][y] = map[y][x] = di ; } scanf("%d", &mother ) ; for( i=1; i<=mother; ++i ) scanf("%d", &mo[i] ) ; sum = flag = 0 ; //妈妈说第一条路必须走,因为她说她在看着我 //但是最后还是要减掉的,所以要走,但是不加上。 for( i=2; i<mother; ++i ){ //如果没路,就输出 N !妈妈和我开玩笑呢! // 继续下一次读入信息 if( map[mo[i]][mo[i+1]] == Max ){ printf("N\n") ; flag = 1 ; break ; } //按照妈妈说的路线走,看看要走多远 sum += map[mo[i]][mo[i+1]] ; } if( !flag ) Dijstra( ) ; } return 0 ; }
题目就是给出一个笛卡尔坐标系,然后给出一些点,问题就是要求最小生成树。我以前没有做过这样的,因为这里,一个点到另一个点的距离没有给出。难道要把一个点到所有的点的距离都求出来?那样的话时间复杂度就太高了吧? 我发现我现在还是太贪求速度,我确实是想到了这里,但是我没有去做,去写,而是去找别人的代码,先看看别人是怎么写的,然后我再写。其实,他们和我的算法思想是一样的。我以后要尽力改正这一个不好的习惯。这样非常不利于我学习算法。加油!
#include <stdio.h> #include <math.h> #define DEBUG 1 const double Max = 0x7fffffff ; const int N = 111 ; int n, used[N], closest[N] ; double map[N][N], pos[N][2], dis[N] ;
double Distance( double *a, double *b ) { return sqrt( (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) ) ; }
double Prim( ) { int i, j, index ; double min, len ; for( i=1; i<=n; ++i ){ used[i] = 0 ; dis[i] = map[1][i] ; } used[1] = 1 ; len = 0 ; for( i=1; i<n; ++i ){ min = Max ; for( j=1; j<=n; ++j ){ if( !used[j] && min>dis[j] ){ index = j ; min = dis[j] ; } } used[index] = 1 ; len += dis[index] ; for( j=1; j<=n; ++j ){ if( !used[j] && dis[j]>map[j][index] ) dis[j] = map[j][index] ; } } return len ; }
int main() { #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.txt","r",stdin); freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.txt","w",stdout); #endif int i, j ; while( EOF != scanf("%d", &n ) ){ for( i=1; i<=n; ++i ) for( j=1; j<=n; ++j ) map[i][j] = Max ; for( i=1; i<=n; ++i ) scanf("%lf%lf", &pos[i][0], &pos[i][1] ) ; for( i=1; i<=n; ++i ){ for( j=i+1; j<=n; ++j ){ map[i][j] = map[j][i] = Distance( pos[i], pos[j] ) ; } } printf("%.2lf\n", Prim( ) ) ; } return 0 ; }
虽然这一题已经写过了,但是我觉得这里有问题。因为题目中有这样的一句话: We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected. 这样一来,如果是A-B-C-D,我们能说A和D是相通的吗?我觉得不能,但是参考别人的代码时,他们都没有考虑这一中情况,我也写了一个没有考虑的。竟然过了!不知道为什么。我主要是在练习算法,难道是我理解错了?
#include <stdio.h> #define DEBUG 1 const int Max = 0x7fffffff ; const int N = 111 ; int n, dis[N], used[N], closest[N], map[N][N] ;
int Prim( ) { int i, j, index, min, len ; for( i=1; i<=n; ++i ){ used[i] = 0 ; dis[i] = map[1][i] ; } used[1] = 1 ; len = 0 ; for( i=1; i<n; ++i ){ min = Max ; for( j=1; j<=n; ++j ){ if( !used[j] && min>dis[j] ){ index = j ; min = dis[j] ; } } used[index] = 1 ; len += dis[index] ; for( j=1; j<=n; ++j ){ if( !used[j] && dis[j]>map[j][index] ) dis[j] = map[j][index] ; } } return len ; } int main() { #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.txt","r",stdin); freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.txt","w",stdout); #endif int i, j, x, y, comp ; while( EOF != scanf("%d", &n ) && n ){ for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ){ scanf("%d", &map[i][j] ) ; map[j][i] = map[i][j] ; } } scanf("%d", &comp ) ; for( i=1; i<=comp; ++i ){ scanf("%d%d", &x, &y ) ; map[x][y] = map[y][x] = 0 ; } printf("%d\n", Prim( ) ) ; } return 0 ; }
没什么好说的,还是最小生成树,和上一题一样,没有任何变化。我现在只会prim算法,所以就只用prim写。
#include <stdio.h> #define DEBUG 1 const int N=101 ; const int Max = 0x7fffffff ; int n, map[N][N], dis[N], used[N], closest[N] ;
int Prim( ) { int i, j, index, min, len ; len = 0 ; for( i=1; i<=n; ++i ){ dis[i] = map[1][i] ; used[i] = 0 ; closest[i] = 1 ; } used[1] = 1 ; for( i=1; i<n; ++i ){ min = Max ; for( j=1; j<=n; ++j ){ if( !used[j] && min>dis[j] ){ min = dis[j] ; index = j ; } } used[index] = 1 ; len += dis[index] ; for( j=1; j<=n; ++j ){ if( !used[j] && dis[j]>map[index][j] ){ closest[j] = index ; dis[j] = map[index][j] ; } } } return len ; }
int main() { #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.in","r",stdin) ; freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.out","w",stdout) ; #endif int i, j, x, y, weight, hangshu ; while( scanf("%d", &n) && n ){ for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ){ map[i][j] = Max ; } } hangshu = n*(n-1)/2 ; for( i=1; i<=hangshu; ++i ){ scanf("%d%d%d", &x, &y, &weight ) ; map[x][y] = map[y][x] = weight ; } printf("%d\n", Prim( ) ) ; } return 0 ; }
这是我做的第一个最小生成树的题目。算法思想就是从任意一个点出发,标记为已找到的。然后找到所有与已找到的点相连的边中,权值最小的边。把边的另一个顶点标记为已找到的。然后更新所有与这个刚找到的顶点相邻的顶点的信息。重复这个步骤,直到找到了n-1个顶点。那就组成了一棵最小生成树,就是题目结束了。
#include <stdio.h> #define DEBUG 1 const int Max = 0x7fffffff ; const int N = 30 ; int n, map[N][N], dis[N], used[N] ;
int Prim( ) { int i, j, index, len, min ; len = 0 ; for( i=1; i<=n; ++i ){ // 出发点定义为 1 dis[i] = map[1][i] ; used[i] = 0 ; } // used[]数组作为标志 ,表示已经被标记过的点 used[1] = 1 ; for( i=1; i<n; ++i ){ min = Max ; //找到所有与已标记过的点相连的边中,权值最小的边 for( j=1; j<=n; ++j ){ if( !used[j] && min>dis[j] ){ index = j ; min = dis[j] ; } } used[index] = 1 ; len += dis[index] ; //更新所有与已找到的点相连的边的信息 for( j=1; j<=n; ++j ){ if( !used[j] && dis[j]>map[index][j] ) dis[j] = map[index][j] ; } } return len ; }
int main() { //输入输出的重定向 #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.in","r",stdin) ; freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.out","w",stdout) ; #endif int i, j, d, geshu, weight ; char ch, to ; while( scanf("%d", &n) && n ){ //初始化数据 for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ){ map[i][j] = Max ; } } //读入数据 for( i=1; i<n; ++i ){ //吃掉空格,不然机会造成读入数据错误 getchar() ; scanf("%c %d", &ch, &geshu ) ; for( j=1; j<=geshu; ++j ){ scanf(" %c %d", &to, &weight ) ; // -64 是为了把第一个点,也就是A点,当成第一个点 // 因为A的ASCII是65 ,所以65-64 就是 1 // 建立的是无向图 map[i][to-64] = map[to-64][i] = weight ; } } printf("%d\n", Prim( ) ) ; } return 0 ; }
又是一道典型的最短路径的题目,当然也可以用搜索,我现在在学习最短路径,所以就用Dijsktra来做的,一开始时WA了好几次,是因为这一题是应该抽象出来一个有向图,而我却是用了无向图,所以一直WA。最后改了一下,然后就过了。
#include <stdio.h> #define DEBUG 1 const int N=300 ; const int Max = 10000000 ; int n, map[N][N], used[N], dis[N] ;
void Dijkstra( int a, int b ) { int i, j, index, min ; for( i=1; i<=n; ++i ){ dis[i] = map[a][i] ; used[i] = 0 ; } used[a] = 1 ; for( i=1; i<=n; ++i ){ index = a ; min = Max ; for( j=1; j<=n; ++j ){ if( !used[j] && min > dis[j] ){ min = dis[j] ; index = j ; } } used[index] = 1 ; for( j=1; j<=n; ++j ){ if( dis[j] > dis[index]+map[index][j] ) dis[j] = dis[index]+map[index][j] ; } } if( dis[b] == Max ){ //printf("%d\n", dis[b] ) ; printf("-1\n") ; } else printf("%d\n", dis[b] ) ; }
int main( ) { #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.in","r",stdin) ; freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.out","w",stdout) ; #endif int i, j, a, b, temp ; while( scanf("%d", &n) && n ){ for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ) map[i][j] = Max ; map[i][i] = 0 ; } scanf("%d%d", &a, &b ) ; for( i=1; i<=n; ++i ){ scanf("%d", &temp ) ; if( i - temp > 0 ) map[i][i-temp] = 1 ; if( i + temp <= n ) map[i][i+temp] = 1 ; } Dijkstra( a, b ) ; } return 0 ; }
#include <stdio.h> #define DEBUG 1 const int N=300 ; const int Max = 10000000 ; int n, map[N][N], used[N], dis[N] ;
void Dijkstra( int a, int b ) { int i, j, index, min ; for( i=1; i<=n; ++i ){ dis[i] = map[a][i] ; used[i] = 0 ; } used[a] = 1 ; for( i=1; i<=n; ++i ){ index = a ; min = Max ; for( j=1; j<=n; ++j ){ if( !used[j] && min > dis[j] ){ min = dis[j] ; index = j ; } } used[index] = 1 ; for( j=1; j<=n; ++j ){ if( dis[j] > dis[index]+map[index][j] ) dis[j] = dis[index]+map[index][j] ; } } if( dis[b] == Max ){ //printf("%d\n", dis[b] ) ; printf("-1\n") ; } else printf("%d\n", dis[b] ) ; }
int main( ) { #if DEBUG freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.in","r",stdin) ; freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.out","w",stdout) ; #endif int i, j, a, b, temp ; while( scanf("%d", &n) && n ){ for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ) map[i][j] = Max ; map[i][i] = 0 ; } scanf("%d%d", &a, &b ) ; for( i=1; i<=n; ++i ){ scanf("%d", &temp ) ; if( i - temp > 0 ) map[i][i-temp] = 1 ; if( i + temp <= n ) map[i][i+temp] = 1 ; } Dijkstra( a, b ) ; } return 0 ; }
这一星期我在学习单源最短路径,今天是这一星期的最后一天。我半写半参考的写出了这一题,要用到DFS+Dijsktra。
题目是让求一个点到另一个点的“前进的”(就是越走离家越近,这是一个难点,不然就做不出来)路径的条数。算法思想是:先求出家到每一个顶点的最短路径,然后用深搜求出从公司到家的路径的条数。
#include <stdio.h> #include <memory.h> #define debug 1 #define N 1010 //要注意不要把Max定义的太大,不然会在做加法时溢出, // 当然,要是不做加法,就没事了,可以定义为2147483647 //也可以用 __int64 ,但是那样的效率比较低 #define Max 10000000
int n ; int map[N][N], dis[N], used[N], num[N] ;
void Input( ) { int i, j, x, m, y, distance ; for( i=1; i<=n; ++i ){ for( j=1; j<=n; ++j ){ map[i][j] = Max ; } //自己到自己的最短路径是0 map[i][i] = 0 ; } scanf("%d", &m ) ; for( i=0; i<m; ++i ){ scanf("%d%d%d", &x, &y, &distance ) ; //用无向图来存储数据 map[x][y] = distance ; map[y][x] = distance ; } }
void Dijstra( int v ) { int i, j, min, index ; for( i=1; i<=n; ++i ){ //求家到各个顶点的初始路径的距离,如果没有路径,就是Max dis[i] = map[v][i] ; //用来标记是否已经求过 used[i] = 0 ; } // 从家里出发,家已经走过,标记为1,下同 used[v] = 1 ; for( i=1; i<=n; ++i ){ min = Max ; index = v ; //找到与每一个已标记过的顶点之间距离最小的顶点 for( j=1; j<=n; ++j ){ if( !used[j] && min > dis[j] ){ index = j ; min = dis[j] ; } } //已经找到,标记 used[index] = 1 ; //更新与刚找到的顶点相邻的顶点的最短路径 for( j=1; j<=n; ++j ){ if( dis[j] > dis[index] + map[index][j] ) dis[j] = dis[index] + map[index][j] ; } } }
int DFS( int nod ) { int i, count ; //如果已到家,就返回1,表示找到了一条路 if( nod == 2 ) return 1 ; //如果这一点已经求过,就返回这一点到家的路径的条数 if( num[nod] > 0 ) return num[nod] ; count = 0 ; //对每一个顶点进行判断,并求路径的条数 for( i=1; i<=n; ++i ){ if( map[i][nod]<Max && dis[i]<dis[nod] ) count += DFS( i ) ; } //标记路径的条数,并返回值 num[nod] = count ; return count ; }
int main() { // 输入输出的重定向,便于调试,在提交时要把debug改成0 #if debug freopen("C:\\Documents and Settings\\Administrator\\桌面\\in.in","r",stdin) ; freopen("C:\\Documents and Settings\\Administrator\\桌面\\out.out","w",stdout) ; #endif while( scanf("%d",&n) && n ){ //读入数据 Input( ) ; //调用Dijsktra 求家到每一个节点的最短路径 Dijstra( 2 ) ; // 数据初始化 memset( num, 0, sizeof(num) ) ; // 用深搜求的路径的条数 printf("%d\n", DFS( 1 ) ) ; } return 0 ; }
#include <iostream> using namespace std; const int lmax=10000; //c1是用来存放展开式的系数的,而c2则是用来计算时保存的, //他是用下标来控制每一项的位置,比如 c2[3] 就是 x^3 的系数。 //用c1保存,然后在计算时用c2来保存变化的值。 int c1[lmax+1],c2[lmax+1]; int main() { int n, i, j, k ; // 计算的方法还是模拟手动运算,一个括号一个括号的计算, // 从前往后 while ( cin>>n ){ //对于 1+x+x^2+x^3+ 他们所有的系数都是 1 // 而 c2全部被初始化为0是因为以后要用到 c2[i] += x ; for ( i=0; i<=n; i++ ){ c1[i]=1; c2[i]=0; } //第一层循环是一共有 n 个小括号,而刚才已经算过一个了 //所以是从2 到 n for (i=2; i<=n; i++){ // 第二层循环是把每一个小括号里面的每一项,都要与前一个 //小括号里面的每一项计算。 for ( j=0; j<=n; j++ ) //第三层小括号是要控制每一项里面 X 增加的比例 // 这就是为什么要用 k+= i ; for ( k=0; k+j<=n; k+=i ){ // 合并同类项,他们的系数要加在一起,所以是加法,呵呵。 // 刚开始看的时候就卡在这里了。 c2[j+k] += c1[j]; } // 刷新一下数据,继续下一次计算,就是下一个括号里面的每一项。 for ( j=0; j<=n; j++ ){ c1[j] = c2[j] ; c2[j] = 0 ; } } cout<<c1[n]<<endl; } return 0; }
|