1. 程式人生 > >演算法競賽入門經典第3章習題

演算法競賽入門經典第3章習題

3-1. 得分(UVa1585)

  給出一個有O和X組成的串(長度為1~80),統計得分。每個O的得分為目前連續出現的O的個數,X的得分為0。例如,OOXXOXXOOO的得分是為1+2+0+0+1+0+0+1+2+3。按照題目說的做就行了,超簡單。
#include<stdio.h>
#include<string.h>
#define Maxn 80
char s[Maxn];
int main() {
	scanf("%s", s);
	int score = 0, tot = 0;
	for(int i = 0; i < strlen(s); i++)
	{
		if(s[i]=='X')
			score = 0;
		else if(s[i]=='O')
			score++;
		tot += score;
	}
	printf("%d\n", tot);
	return 0;
}

3-2.分子量(UVa1586)

  給出一種物質的分子式(不帶括號),求分子量。本題的分子式只包含4種原子,分別為C,H,O,N,原子量為12.01,1.008,16.00,14.01(單位:g/mol)。例如,C6H5OH的分子量為94.108 g/mol。按照題目做就行了。
#include<stdio.h>
#include<string.h>
#define Maxn 80
int main() {
	char s[Maxn];
	scanf("%s", s);
	float tot = 0, score = 0;
	int num;
	for(int i = 0; i < strlen(s); i++)
	{	
		
		switch(s[i])
		{
			case 'C': {score = 12.01;	break;}
			case 'H': {score = 1.008;	break;}
			case 'O': {score = 16.00;	break;}
			case 'N': {score = 14.01;	break;}
		}
		if(s[i+1] > '0' && s[i+1] <= '9')
		{
			num = s[i+1] - '0';
			i++;
		}
		tot += score * num;
		num = 1;
	}
	printf("%.3f\n", tot);
	return 0;
}

3-3 數數字(UVa1225)

  把前n(n <= 10000)個整數順次寫在一起:123456789101112...數一數0~9各出現多少次(輸出10個整數,分別是0,1,2,..., 9出現的次數)。
#include<stdio.h>
#include<string.h>
#define Maxn 10005
char s[Maxn];
int a[10];
int main() {
	scanf("%s", s);
	for(int i = 0; i < strlen(s); i++)
		a[s[i]-'0'] ++;

	for(int i = 0; i < 10; i++)
		printf("%d  ", a[i]);
	printf("\n");
	return 0;
}

3-4 週期串 (Uva455)

  如果一個字串可以由某個長度為k的字串重複多次得到,則稱該串以k為週期。例如,abcabcabcabcabc 以3為週期(注意,它也以6 和 12 為週期)。輸入一個長度不超過80的字串,輸出其最小週期。解法就是: 從小到大列舉各個週期(即從1到字串大小),一旦符合條件就立即輸出。當不是週期串時輸出的最小週期即為其本身長度。
#include<stdio.h>
#include<string.h>
#define maxn 101
char s[maxn];
int main() {
    scanf("%s", s);
    int i, j, flag = 1; /* i記錄週期的長度, j記錄第二週期開始的位置*/
    int len = strlen(s);
    for(i = 1; i <= len; i++)
        if(len % i == 0)    //字串的長度肯定可以被週期整除
        {
            flag = 1;
            //檢測是否有周期性
            for(j = i; j < len; j++)
            {
                if(s[j] != s[j%i])
                {
                    flag = 0;
                    break;
                }
            }
            if(flag)    //如果是週期,即刻跳出
                break;
        }
    printf("%d", i);
    return 0;
}


3-5 謎題(UVa227)

有一個5*5的網路,其中恰好有一個格子是空的,其他格子各有一個字母。一個有4種指令:A, B, L, R, 分別表示把空格上、下、左、右的相鄰字母移到空格中。輸入初始網格和指令序列(以數字0結束),輸出指令執行完畢後的網路。如果有非法指令,應輸出" This puzzle has no final configuration."。
執行ARRBBL0前
T R G S J
X D O K I
M   V L N
W P A B E
U Q H C F
執行ARRBBL0後
T R G S J
X O K L I
M D V B N
M P   A E
U O H C F

             解法:輸入初始網格和指令序列,初始網格用二維陣列表示,分別按照指令順序進行將字母 移到空格,遇到0就結束。






#include<stdio.h>
#include<string.h>
#define n 5
char s[n][n];
int main() {
    int i, j;
    char c;
    int i_Null = 0, j_Null = 0;
    for(i = 0; i < n; i++)
        for(j = 0; j < n; j++)
            {
                scanf("%c", &s[i][j]);
                if(s[i][j] == '*')
                    {i_Null = i; j_Null = j;}
            }
    fflush(stdin);
   while((c = getchar()) != EOF){
       if(c == '0') break;
       switch(c)
       {
           case 'A': {s[i_Null][j_Null] = s[i_Null-1][j_Null]; s[i_Null-1][j_Null] = '*'; i_Null--; break;}
            case 'B': {s[i_Null][j_Null] = s[i_Null+1][j_Null]; s[i_Null+1][j_Null] = '*'; i_Null++;break;}
             case 'L': {s[i_Null][j_Null] = s[i_Null][j_Null-1]; s[i_Null][j_Null-1] = '*'; j_Null--;break;}
              case 'R': {s[i_Null][j_Null] = s[i_Null][j_Null+1]; s[i_Null][j_Null+1] = '*'; j_Null++;break;}
              default: {printf("This puzzle has no final configuration."); return 0;}
       }
   }
   printf("\n");
    for(i = 0; i < n; i++) {
        for(j = 0; j < n; j++)
            printf("%c  ", s[i][j]);
        printf("\n");
    }
    return 0;
}

3-6 縱橫字謎的答案(Uva232)

    輸入一個r行c列(1<=r, c<=10)的網格,黑格用“*”表示,每個白格都填有一個字母。如果一個白格的左邊相鄰位置或者上邊相鄰位置沒有白格(可能是黑格,也可能出了網格邊界)則稱這個白格是一個起始格。首先把所有的起始格按照從上到下的、從左到右的順序編號為1,2,3,···,如圖所示:
r行c列網格
1 2 3 * 4 5 6
* 7   * 8    
9     10 * 11  
12   * 13 14    
* 15 16 * 17   *
18     * 19   20
接下來要找出所有橫向單詞(Across)。這些單詞必須從一個起始格開始,向右延伸到一個黑格的左邊或者整個網格的最右邊。最後找出所有豎向單詞(Down)。這些單詞必須從一個起始格開始,向下延伸到一個黑格的上邊或者整個網格的最下行。

#include<stdio.h>
#include<string.h>
#define MAX 12
int main(void)
{
    char buf[MAX][MAX];   //用來儲存字母
    int num[MAX][MAX];    //用來儲存數字
    int r, c;
    int count=1, i, j;
    while(scanf("%d", &r)&&r!=0)
    {
        scanf("%d", &c);
        memset(num, 0, sizeof(num));
        for(i = 0; i < r; i++)
            scanf("%s", buf[i]);
        int m = 1;
        for(i = 0; i < r; i++)
        {
            for(j = 0; j < c; j++)
            {
                if(buf[i][j] == '*') //如果這個位置是黑格,不用填
                    continue;
                if((j-1) < 0 || buf[i][j-1] == '*' || (i-1) < 0 || buf[i-1][j] == '*')
                {   //這個位置的上面或左面是黑格或是邊界,開始往num填數字
                    num[i][j] = m++;
                }
            }
        }
        if(count != 1)
            printf("\n");
        printf("puzzle #%d:\n", count++);
        printf("Across\n");
        for(i = 0; i < r; i++)
        {
            j = 0;
            while(j < c)
            {
                if(num[i][j] == 0 || buf[i][j] == '*')
                {//如果這個是黑格或中間無值格,跳過這一次
                    j++;
                    continue;
                }
                printf("%3d.%c", num[i][j], buf[i][j]);//格式需要
                j++;
                while(j < c && buf[i][j] != '*') //當輸出到行結尾時結束或是黑格時
                {
                    printf("%c", buf[i][j]);
                    j++;
                }
                printf("\n");
            }
        }
        printf("Down\n");
        for(i = 0; i < r; i++)
        {
            for(j = 0; j < c; j++)
            {
                if(num[i][j]==0 || buf[i][j]=='*')
                    continue;
                printf("%3d. %c", num[i][j], buf[i][j]);
                num[i][j] = 0;
                int k = i + 1;
                while(k < r && buf[k][j] != '*')
                {
                    printf("%c", buf[k][j]);
                    num[k][j] = 0;
                    k++;
                }
                printf("\n");
            }
        }
    }
    return 0;
}

3-7 DNA序列

  輸入m個長度均為n的DNA序列,求一個DNA序列,到所用序列的總Hamming距離儘量小。兩個等長字串的Hamming距離等於字元不同的位置個數,例如,ACGT和GCGA的Hamming距離為2(左數第1,4個字元不同)。   輸入整數m和n(4<=m<=50, 4<=n<=100),以及m個長度為n的DNA序列(只包含字母A,C,G,T),輸出到m個序列的Hamming距離和最小的DNA序列和對應的距離。如有多解,要求為字典序最小的結,例如,對於下面5個DNA序列,最優解為TAAGATAC。 TATGATAC TAAGCTAC AAAGATCC TGAGATAC TAAGATGT