POJ 做過的算法題彙總


看了一下poj筆記記錄第一道題的時間,最早的一道題距今有5個月了,當時還在看Linux下的驅動,只是抱着有趣的心態,接觸了下poj。在前幾天把劍指offer看完之後,打算將算法題歸類,按各種類別來自己完成。目前發現一個leetcode面試題平臺,打算從poj轉向leetcode,並把數據結構算法做個詳細的總結。下面是以前的poj題總結,也有貼網上看的別人的實現代碼。

1.poj1182

食物鏈

Description

動物王國中有三類動物A,B,C,這三類動物的食物鏈構成了有趣的環形。A吃B, B吃C,C吃A。

現有N個動物,以1-N編號。每個動物都是A,B,C中的一種,但是我們並不知道它到底是哪一種。

有人用兩種說法對這N個動物所構成的食物鏈關係進行描述:

第一種說法是"1 X Y",表示X和Y是同類。

第二種說法是"2 X Y",表示X吃Y。

此人對N個動物,用上述兩種說法,一句接一句地說出K句話,這K句話有的是真的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。

1) 當前的話與前面的某些真的話衝突,就是假話;

2) 當前的話中X或Y比N大,就是假話;

3) 當前的話表示X吃X,就是假話。

你的任務是根據給定的N(1 <= N <= 50,000)和K句話(0 <= K <= 100,000),輸出假話的總數。

Input

第一行是兩個整數N和K,以一個空格分隔。

以下K行每行是三個正整數 D,X,Y,兩數之間用一個空格隔開,其中D表示說法的種類。

D=1,則表示X和Y是同類。

D=2,則表示X吃Y。

Output

只有一個整數,表示假話的數目。

Sample Input

100 7

1 101 1

2 1 2

2 2 3

2 3 3

1 1 3

2 3 1

1 5 5

Sample Output

 

 

 

 

 

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

//#define INPUT 1

//#define DEBUG 1//與INPUT一起,調試時用來顯示每一步變化

int par[5001];//如果不用固定數組的話,可以改爲結構體

int rank[5001];//與父節點的關係

/*

0:同類

1:被父節點吃

2:吃父節點

int input[100001][3];

 

void initial(int);

int find(int);//查找並鏈接到根上,rank中也隨時更新

void link(int, int, int);//鏈接兩個根節點

int main()

{

int anim_num, words_num;

int i,j, count = 0;

#ifdef INPUT

    freopen("I:\\學習日誌\\linux驅動之路\\input.txt","r",stdin);//U盤中的路徑

#endif

scanf("%d%d",&anim_num, &words_num);

for(i = 0; i<words_num; i++){

scanf("%d%d%d",&input[i][0], &input[i][1], &input[i][2]);

    }

    initial(anim_num);//只初始化用的那部分

    for(i = 0; i<words_num; i++){

if(input[i][1] > anim_num ||input[i][2] > anim_num || (input[i][0] == 2 && input[i][1] == input[i][2]))//數字超出範圍,或自己吃自己都記作錯誤

count++;

else

{

if(find(input[i][1]) == find(input[i][2])){//如果兩動物在一個集合裏,另外,這裏每find一次,會調整一次樹的結構

                if((3-rank[input[i][1]]+rank[input[i][2]])%3 != (input[i][0]-1))//通過共有的根節點來推導兩節點的關係

    	count++;

            }

else

link(input[i][1], input[i][2], input[i][0]);//不在一個集合,根本無法判斷對錯,只能當成對的將兩個集合拼接到一塊

}

#ifdef DEBUG

        for(j =1;j<=anim_num; j++)

            printf("%4d",par[j]);

        printf("\n");

        for(j=1;j<=anim_num; j++)

            printf("%4d",rank[j]);

        printf("\n*********\n");

#endif

}

printf("%d\n",count);

return 0;

}

 

void initial(int all)

{

int i;

for(i = 0; i < all+1; i++){

par[i] = i;

        rank[i] = 0;

}

}

int find(int num)

{

int j;

while(par[num] != par[par[num]]){

j = par[num];

        	par[num] = par[j];

rank[num] = (rank[num] + rank[j])%3;

}

return par[num];

}

void link(int a, int b, int relation)

{

    	int temp = find(b);

par[temp] = find(a);

rank[temp] = (3 - rank[b] + rank[a] + relation - 1)%3;//逆向推導兩節點關係

}

這裏的find直接用的while,沒有使用遞歸,遞歸的邏輯會更清晰。

1.使用並查集的目的就是爲了在搜索的時候壓縮路徑。

2.表示節點之間關係的時候,只有三種關係,數字來表示,根據節點間的關係,先找出各個路徑跳躍節點的關係,即路徑壓縮的方式。

3.當需要將兩個根節點連接到一起,爲了推斷根節點的關係,可以逆序,從起始節點開始,通過集合內部路徑到達目的節點,逆序找出根節點關係。

4.如何判斷對錯,在同一個集合裏的時候,通過兩個節點相對於根節點的關係,推出兩節點關係與給出的語句對比;若不在一個集合裏面,即新的條件,此時不管怎麼判斷都是對的。

5.另外,在每次查找根節點時,我都將查找的樹接到根節點上,減小樹的深度,在下次查找時加快搜索速度。

6.寫之前務必要想清楚再寫。

 2.poj1308

題目:

Is It A Tree?

Time Limit: 1000MS   Memory Limit: 10000K

Total Submissions: 9821   Accepted: 3354

Description

A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties.

 

There is exactly one node, called the root, to which no directed edges point.

Every node except the root has exactly one edge pointing to it.

There is a unique sequence of directed edges from the root to each node.

For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not.

/*此處缺個圖*/

In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.

Input

The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero.

Output

For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).

Sample Input

6 8  5 3  5 2  6 4

5 6  0 0

 

8 1  7 3  6 2  8 9  7 5

7 4  7 8  7 6  0 0

 

3 8  6 8  6 4

5 3  5 6  5 2  0 0

-1 -1

Sample Output

Case 1 is a tree.

Case 2 is a tree.

Case 3 is not a tree

分析:自己寫題的一些想法,在每組的所有數據未讀取前,並不能夠判斷,而如果用並查集的話,在數據輸入完畢後,需要對樹再遍歷一遍以判斷真假,正是這個遍歷,導致了速度會變的很慢。

第一次的程序,可以正常運行,但速度慢,中間用了兩次循環。邏輯不全,需要多注意的幾組數據:

> 1: 0 0 空樹是一棵樹

> 2: 1 1 0 0 不是樹 不能自己指向自己

> 3: 1 2 1 2 0 0 不是樹....自己開始一直在這麼WA  好鬱悶 重複都不行呀~~5555

> 4: 1 2 2 3 4 5 不是樹  森林不算是樹(主要是注意自己)

> 5: 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 1  注意 一個節點在指向自己的父親或祖先 都是錯誤的 即 9-->1 錯

> 6: 1 2 2 1 0 0 也是錯誤的

 

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

//#define INPUT 1

//#define DEBUG 1

#define MAXNUM 5001

int par[MAXNUM];

int input[MAXNUM][2];

 

void initial(void);

int find(int);

int main()

{

int count = 1, i = 0;

    int treebegin = 0, sum = 0;

#ifdef INPUT

    freopen("I:\\input.txt","r",stdin);

#endif

do{

scanf("%d%d",&input[i][0], &input[i][1]);

   

    }while(input[i++][0] != -1 && input[i][1] != -1);

  

    initial();

    sum = i - 1;

    for(i = 0; i < sum;){

        if((par[input[i][1]] != input[i][0]) &&\

              (par[input[i][1]] != input[i][1])){

            printf("Case %d is not a tree.\n",count++);

            initial();

            while((input[i][0] != 0) && (input[i][1] != 0))

                i++;

            treebegin = ++i;

        }

        else if(input[i][0] == 0 && input[i][1] == 0){

                for(; treebegin < i-1; treebegin++){

                    if(find(input[treebegin][1]) != \

                        find(input[treebegin+1][1])){

                      printf("Case %d is not a tree.\n",count++);

                        treebegin = -1;

                        break;

                    }

                }

                if(treebegin != -1)

                    printf("Case %d is a tree.\n",count++);

                i++;

                treebegin = i;

                initial();

             }

            else

                par[input[i++][1]] = input[i][0];

    }

    return 0;

}

 

void initial(void)

{

    int i;

for(i = 0; i < MAXNUM; i++){

par[i] = i;

}

}

int find(int num)

{

    while(par[num] != num)

        num = par[num];

    return num;

}

 

第二種:

仔細思考了一下,這種近乎無懈可擊,畢竟樹就是這樣的。

利用樹的定義,即邊數加一等於頂點數

#include<cstdio>     //頂點的個數==邊的個數+1

#include<cstring>

const int MAXN=1000;

int par[MAXN];

int main()

{

    int m,n;

    int t=1;

    while(scanf("%d%d",&m,&n)&&m>=0&&n>=0){

        memset(par,0,sizeof(par));

        int e=1;

        if((m==0&&n==0)){   //空樹

            printf("Case %d is a tree.\n",t++);

            continue;

        }

        par[m]=par[n]=1;

        while(scanf("%d%d",&m,&n)&&m>0&&n>0){

            e++;

            par[m]=par[n]=1;

        }

        int v=0;

        for(int i=0;i<MAXN;i++)

            v+=par[i];

        int flag=(v==e+1);

        printf("Case %d ",t++);

        if(flag)

            puts("is a tree.");

        else

            puts("is not a tree.");

    }

    return 0;

}

第三種:

正常的樹結構來判斷,考慮了一下,相當麻煩,先放一下,路上再想,得看其他的了,這個

這裏有個簡單的並查集

#include<iostream>

#include<cstdio>

#include<cstring>

#include<set>

using namespace std;

 

 

int num=1;

set<int>s;

int pre[100000+100];

int flag;

void init()

{

    for(int i=1;i<=100000;i++) pre[i]=i;

    flag=0;

    s.clear();

}

 

int find(int x)

{

    return x==pre[x]?x:pre[x]=find(pre[x]);//嵌套,寫起來比循環好多了,而且容易理解,但這不是目的。記住這種寫法,要隨手就能寫出來。

}

 

int main()

{

    int x,y;

    init();

    while(~scanf("%d%d",&x,&y)&&(x!=-1&&y!=-1))//正常輸入,沒到結尾

    {

        if(x==0&&y==0)//判斷一組數據結束

        {

            if(flag==0){

            	int cnt=0;

            	for(set<int>::iterator it=s.begin();it!=s.end();it++){

                	if(pre[*it]==*it) cnt++;//統計root的數量

            	}

            	if(cnt>1)//只能由一個root。

flag=1;

            }

           if(flag)

printf("Case %d is not a tree.\n",num++);

           else

printf("Case %d is a tree.\n",num++);

           init();

           continue;

        }

        else if(!flag)

        {

            s.insert(x);

            s.insert(y);

            int px=find(x);

            int py=find(y);

            if(px==py) flag=1;

            else if(y!=find(y)){//和我想法不同的就是這一句,它保證了掛到樹上的是一個子樹而不是子樹的分支。

                flag=1;

            }

            else pre[py]=px;

        }

    }

}

在我看來,這個寫的賊好,我的思路和這個基本一致,但寫出來的代碼運行起來毛病就是多。

 

3. poj Judging Olympia

Judging Olympia

Time Limit: 1000MS Memory Limit: 65536K

Total Submissions: 8878 Accepted: 4205

Description

 

For years, a group of Regional Contest Directors (RCDs) of the ACM International Collegiate Programming Contest (ICPC) have been unsatisfied with the way contest submissions get ranked. The group sees it is academically wrong to emphasize the importance of program correctness, disregarding the “quality” of the program itself. After all, programming as a profession promotes design, style, maintainability, etc. and not just correctness. The group’s suggestion is to have a panel of six judges. Each judge is assigned the task of grading the submissions based on a particular aspect: 1) Correctness; 2) Robustness; 3) Overall design; 4) Clarity; 5) Coding style; and finally 6) Maintainability. The final grade of a submission would be the average of the six grades it gets.

 

The old guards of the current ICPC judging style have always responded that it is not possible to impartially judge a program on anything but correctness. How can the ICPC be certain that judging is fair? In other words, how can the ICPC be sure that non of the judges is favoring certain teams and disadvantaging others? Any hint of accusation to the judging process and ICPC loses the prestigious status it worked on for years. (Alright! So they do have a point.) Still, this hasn’t stopped other domains from judging candidates based on subjective metrics. Take for example Gymnastics, or The Nobel Prizes, or even the ACM’s very own Doctoral Dissertation Award. These are all highly respected awards where the winner is selected by judges using subjective metrics. ICPC could use a new judging system based on what is used in gymnastics. Rather than having each judge grade a certain aspect of the program, each of the six judges would assign an overall grade (out of ten) based on all of the six metrics mentioned above. To enforce impartiality, the final grade of a submission would be calculated as the average of all the grades after deleting two grades: The highest and the lowest. Any judge that favors a certain team (and assigns them an undeserved high grade,) risks the possibility of that grade being dismissed. Similarly, any judge that attempts to disadvantage a team by assigning them a low grade faces a similar risk.

 

Write a program to print the final grade of a submission.

 

Input

 

Your program will be tested on one or more test cases. Each test case is described on a single input line listing the grades of the judges. The end of the test cases is identified with a dummy test case with all the grades being zero.

 

Output

 

For each test case, print the grade on a separate line (without unnecessary decimal points and/or zeros.)

 

Sample Input

 

8 8 8 4 4 4

8 8 6 4 4 3

0 0 0 0 0 0

Sample Output

 

6

5.5

Source

 

Arab and North Africa 2007

#include <stdio.h>

//#include <string.h>

#include <stdlib.h>

//#define INPUT 1

int lineaverage();

int main(void)

{

#ifdef INPUT

    freopen("I:\\input.txt","r",stdin);

#endif

    while(!lineaverage())

        ;

    return 0;

}

int lineaverage(){

    int i, count = 0, min =0, max =0, sum = 0;

    char *ch;

    while((ch=getchar())!= '\n' && ch != '0' ){

        if(ch == ' ')

            continue;

        i = ch - '0';

        if( max == 0)

            max = i;

        if(i> max)

            max = i;

        if(min == 0)

            min = i;

        if(i< min)

            min = i;

        sum +=i;

        count++;

    }

    if(ch != '0')

        printf("%4.2f\n", (float)(sum -(count>2?max+min:0))/(count>2?count -2:count));

    return ch == '\n' ? 0 :1;

}

int read()

{

    char ch;

    if((ch = getchar())== ' ')

        return 0;

    else

 

}

 

4. poj1401階乘統計

 

The most important part of a GSM network is so called Base Transceiver Station (BTS). These transceivers form the areas called cells (this term gave the name to the cellular phone) and every phone connects to the BTS with the strongest signal (in a little simplified view). Of course, BTSes need some attention and technicians need to check their function periodically.

 

ACM technicians faced a very interesting problem recently. Given a set of BTSes to visit, they needed to find the shortest path to visit all of the given points and return back to the central company building. Programmers have spent several months studying this problem but with no results. They were unable to find the solution fast enough. After a long time, one of the programmers found this problem in a conference article. Unfortunately, he found that the problem is so called "Travelling Salesman Problem" and it is very hard to solve. If we have N BTSes to be visited, we can visit them in any order, giving us N! possibilities to examine. The function expressing that number is called factorial and can be computed as a product 1.2.3.4....N. The number is very high even for a relatively small N.

 

The programmers understood they had no chance to solve the problem. But because they have already received the research grant from the government, they needed to continue with their studies and produce at least some results. So they started to study behaviour of the factorial function.

 

For example, they defined the function Z. For any positive integer N, Z(N) is the number of zeros at the end of the decimal form of number N!. They noticed that this function never decreases. If we have two numbers N1 < N2, then Z(N1) <= Z(N2). It is because we can never "lose" any trailing zero by multiplying by any positive number. We can only get new and new zeros. The function Z is very interesting, so we need a computer program that can determine its value efficiently.

 

Input

 

There is a single positive integer T on the first line of input. It stands for the number of numbers to follow. Then there is T lines, each containing exactly one positive integer number N, 1 <= N <= 1000000000.

Output

 

For every number N, output a single line containing the single non-negative integer Z(N).

Sample Input

 

6

3

60

100

1024

23456

8735373

Sample Output

 

0

14

24

253

5861

2183837

 

分析:統計所有的0,在質數裏只有2、5會出現0的情況,十進制中,沒進一位就有一個2、5因子,故統計階乘數字中2、5的數目,能夠組成多少對2/5,就有多少個10,即0。這是我最初的思路,但是在看其他人用的代碼後發現5的個數小於2,所以只統計5的數目就好

int count(int num, int i);

int main(void)

{

    unsigned int n, num, n2m, n5m,temp;

    scanf("%d",&n);

    while(n--){

        scanf("%d", &num);

        n2m = 0;

        n5m = 0;

        while(num){

            n2m += count(num, 2);

            n5m += count(num, 5);

            num--;

        }

        printf("%d\n",n2m>=n5m?n5m:n2m);

    }

    return 0;

}

int count(int num, int i)

{

    int count = 0;

    while(num%i == 0){

        count++;

        num /= i;

    }

    return count;

}

 

別人的:

#include<stdio.h>  

int main()  

{  

    int t,n,i;  

    int sum;  

    scanf("%d",&t);  

    while(t--)  

    {  

        sum = 0;  

        scanf("%d",&n);  

        for(i=5;i<=n;i*=5) //只計算5的個數,而且只用到了/

        {  

            sum+=n/i;  

        }  

        printf("%d/n",sum);  

    }  

}  //他的這個不僅僅是隻統計5因子的數目,而且計算量也小的多,改成他這種。

 

把自己的修改一下:

#include <stdio.h>

int main(void)

{

    unsigned int n, num,i ,count;

    scanf("%d",&n);

    while(n--){

        scanf("%d", &num);

        count=0;

        i = 5;

        while(i <= num){

            count+= (num/i);

            i *= 5;

        }

        printf("%d\n",count);

    }

    return 0;

}

看看別人的,只能說好6

5. poj1664蘋果放盤子問題

放蘋果

Time Limit: 1000MS   Memory Limit: 10000K

Total Submissions: 15927   Accepted: 10049

Description

 

M個同樣的蘋果放在N個同樣的盤子裏,允許有的盤子空着不放,問共有多少種不同的分法?(用K表示)5,1,1和1,5,1 是同一種分法。

Input

第一行是測試數據的數目t(0 <= t <= 20)。以下每行均包含二個整數M和N,以空格分開。1<=M,N<=10。

Output

對輸入的每組數據M和N,用一行輸出相應的K。

Sample Input

1

7 3

Sample Output

8

 

高中學的排序組合全忘光了。

放蘋果的問題:

這類與另一類相似,即將整數m分爲不超過n(整數)個數的和。情況分三種:

m>n , m=n, m<n

其中:

m>n時,盤子沒有空的,即n個盤子每個上面放一個,剩下的m-n個往n個盤子上放;盤子至少空一個,即將m個放到n-1個盤子上。

m=n時,與(m, n-1)相比,就多了一了每個盤子放一個的情況。

m<n時,等同於m個往m個裏放。

另外,再考慮下遞歸的終極條件,m=1或n=1;

 

#include <stdio.h>

int sort(int, int);

int main(void)

{

    int t, m, n;

    scanf("%d",&t);

    while(t--){

        scanf("%d%d",&m, &n);

        printf("%d", sort(m, n));//計算所有組合的種類

    }

    return 0;

}

int sort(int m, int n)

{

    if(m == 1 || n == 1)

        return 1;

    if(m<1 || n<1)

        return 0;

    if(m>n)

        return sort(m-n, n) + sort(m, n-1);//

    if(m==n)

        return 1+sort(m, n-1);

    if(m<n)

        return sort(m, m);

}

 

 

6. poj1562Oil Deposits

poj1562

Oil Deposits

Time Limit: 1000MS   Memory Limit: 10000K

Total Submissions: 6282   Accepted: 3557

Description

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid.

Input

The input contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either `*', representing the absence of oil, or `@', representing an oil pocket.

Output

are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets.

Sample Input

1 1

*

3 5

*@*@*

**@**

*@*@*

1 8

@@****@*

5 5

****@

*@@*@

*@**@

@@@*@

@@**@

0 0

Sample Output

0

1

2

2

 

分析:使用dfs深度優先搜索,從一個點出發,查找下一個相關點,再走下一個點。

另外,遞歸有時特好用。

忽略的點:

1。scanf給char型賦值,需要注意換行符、空格

2.在循環中, 使用+=的應該是累積量

#include <stdio.h>

 

char oil_region[100][100];

int dir[8][2] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};//好多地方可以用到這個技巧

int dfs(int, int);

int row, col;

 

int main(void)

{

    int cnt;

    int i, j;

    char temp;

 

    scanf("%d%d",&row, &col);

    while(row != 0 && col != 0) {

        cnt = 0;

        for(i = 0; i<row; i++)

            for(j = 0; j<col; j++){

                while((temp = getchar()) == ' ' || temp == '\n')

                    ;

                oil_region[i][j] = temp;

            }

        

        for(i = 0; i<row; i++){

            for(j = 0; j<col; j++)

                if(oil_region[i][j] == '@'){

                    dfs(i, j);

                    cnt++;

                }

        }

        printf("%d\n",cnt);

        scanf("%d%d",&row, &col);

    }

    return 0;

}

int dfs(int i, int j)

{

    int k, a ,b;

 

    oil_region[i][j] = '*';//將已查到的@改爲*,這相當於把已經搜過的path做個標記

    for(k=0; k<8; k++){//開始深搜

        a = dir[k][0] + i;

        b = dir[k][1] + j;

        if(a>=0 && b>=0 && a<row  && b<col  && oil_region[a][b] == '@')

            dfs(a, b);

    }

    return 1;//返回值並沒有什麼用,可以加給cnt

}

 

 

7. poj1651Multiplication Puzzle

 

Multiplication Puzzle

Time Limit: 1000MS Memory Limit: 65536K

Total Submissions: 11353 Accepted: 7034

Description

 

The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row.

 

The goal is to take cards in such order as to minimize the total number of scored points.

 

For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring

10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000

 

If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be

1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150.

Input

 

The first line of the input contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces.

Output

 

Output must contain a single integer - the minimal score.

Sample Input

 

6

10 1 50 50 20 5

Sample Otput

 

3650

 

分析:

記憶化搜索:

    網上說是簡單的數組乘,沒看出來。總之也是遍歷,拆分成最基本的三個數的情況,計算所有的情況,選出最小的。

    用一個二維數組記錄(a, b)的最小值

    在(a, b)之間,假設最後一個要選的數字爲i,則有min(a, b) = min(a, i)+ min(i, b)+x[a]*x[i]*x[b];其中,x[a]*x[i]*x[b]是計算的最後一步。

    但是,目前的情況是不知道i是多少,所以讓i一次等於從a到b之間的每一個數,計算每一次的(a, b)找出最小的。

    當然,中間要用到迭代。

    找到爲什麼說是簡單的數組乘了,另一類題,計算幾個數組相乘合爲一個數組後的乘法量也類似這種。

#include <stdio.h>

#include <string.h>

//#include <stdlib.h>

int rela[101][101];

int temp[101];

int dp(int, int);

int main(void)

{

    int i, n;

    

    scanf("%d",&n);

    for(i=0; i<n; i++)

        scanf("%d",&temp[i]);

    memset(rela, -1, sizeof(rela));

    printf("%d\n",dp(0, n-1));

    return 0;

}

int dp(int a, int b)

{

    int min = 2147483647;

    int temp1, i;

 

    if(rela[a][b] != -1)

        return rela[a][b];

    if(b-a == 1)

        return rela[a][b] = 0;

    for(i = a+1; i< b; i++){//當i爲未知數時,就全部算一遍

        temp1 = (dp(a, i) + dp(i, b) + temp[a]*temp[i]*temp[b]);

        if(temp1<min)

            min = temp1;

    }

        rela[a][b] = min;

    return min;

}

 

記憶化搜索,將一個大的問題拆分爲小問題。將已經搜索過的結果記錄下來。

感覺很像深度優先搜索,搜過的都會做標記。???這倆的區別留給之後的題補上。

這有個娃寫的思路更清晰易懂的代碼:

僅貼上搜索部分:

int dfs(int l,int r){  

    if(r-l<2)return 0;  

    int Min=10000001;

 

    for(int i=l+1; i<r; i++){

        if(mi[l][i]==-1)

mi[l][i]=dfs(l,i);

        if(mi[i][r]==-1)

mi[i][r]=dfs(i,r);

        if(Min>mi[l][i]+mi[i][r]+a[l]*a[i]*a[r])  

Min=mi[l][i]+mi[i][r]+a[l]*a[i]*a[r];  

    }  

return Min;

}

 

 


8. poj1316SelfNumbers

poj1316

Self Numbers

Time Limit: 1000MS Memory Limit: 10000K

Total Submissions: 24458 Accepted: 13676

Description

 

In 1949 the Indian mathematician D.R. Kaprekar discovered a class of numbers called self-numbers. For any positive integer n, define d(n) to be n plus the sum of the digits of n. (The d stands for digitadition, a term coined by Kaprekar.) For example, d(75) = 75 + 7 + 5 = 87. Given any positive integer n as a starting point, you can construct the infinite increasing sequence of integers n, d(n), d(d(n)), d(d(d(n))), .... For example, if you start with 33, the next number is 33 + 3 + 3 = 39, the next is 39 + 3 + 9 = 51, the next is 51 + 5 + 1 = 57, and so you generate the sequence

 

33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

The number n is called a generator of d(n). In the sequence above, 33 is a generator of 39, 39 is a generator of 51, 51 is a generator of 57, and so on. Some numbers have more than one generator: for example, 101 has two generators, 91 and 100. A number with no generators is a self-number. There are thirteen self-numbers less than 100: 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, and 97.

 

Input

 

No input for this problem.

Output

 

Write a program to output all positive self-numbers less than 10000 in increasing order, one per line.

Sample Input

 

Sample Output

 

1

3

5

7

9

20

31

42

53

64

 |

 |       <-- a lot more numbers

 |

9903

9914

9925

9927

9938

9949

9960

9971

9982

9993

這個簡單,一次就過

#include<stdio.h>  

#define MAX 10001  

int flag[MAX];  

int main()  

{  

    memset(flag,0,sizeof(flag));  

    int i;  

    int sum,tmp;  

    for(i=1;i<MAX;i++)  

    {  

        sum=tmp=i;  

        while(tmp)  

        {  

            sum+=tmp%10;  

            tmp/=10;  

        }  

        if(sum<MAX) flag[sum]=1;  

    }  

    for(i=1;i<MAX;i++)  

        if(flag[i]==0) printf("%d/n",i);  

    return 0;  

}  

 

 

9. poj1258Agri-Net

Agri-Net

Time Limit: 1000MS   Memory Limit: 10000K

Total Submissions: 17635   Accepted: 7091

Description

Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet connectivity to all farms in the area. He needs your help, of course.

Farmer John ordered a high speed connection for his farm and is going to share his connectivity with the other farmers. To minimize cost, he wants to lay the minimum amount of optical fiber to connect his farm to all the other farms.

Given a list of how much fiber it takes to connect each pair of farms, you must find the minimum amount of fiber needed to connect them all together. Each farm must connect to some other farm such that a packet can flow from any one farm to any other farm.

The distance between any two farms will not exceed 100,000.

Input

The input includes several cases. For each case, the first line contains the number of farms, N (3 <= N <= 100). The following lines contain the N x N conectivity matrix, where each element shows the distance from on farm to another. Logically, they are N lines of N space-separated integers. Physically, they are limited in length to 80 characters, so some lines continue onto others. Of course, the diagonal will be 0, since the distance from farm i to itself is not interesting for this problem.

Output

For each case, output a single integer length that is the sum of the minimum length of fiber required to connect the entire set of farms.

Sample Input

4

0   4    9   21

4   0    8   17

9   8    0   16

21 17 16   0

Sample Output

28

 

分析:

最小生成樹的結構,經證明,最小生成樹一定包含最小的那個邊,如果不包含的話,添加上這個邊,去掉另一個邊,會形成一個更小的樹,所以肯定包含。k算法每次只需找出一條最小的邊添加進樹,只要不形成環即可。prim算法是從點出發,一點點擴散。

 

因爲只需要輸出最後結果,所以沒有保留關於樹的結構,邊的結構也沒有,畢竟無向。

#include <iostream>

 

int dis[101][101];

int tree[101];

int n, tempi, tempj, min;

void init(void);

void input(void);

void find(void);

int cmp(int i, int j);

 

int main(int argc, char **argv[])

{

    int i, j, sum = 0;

 

    init();

    std::cin >> n;

    input();

    for(i = 0; i<n-1; i++){//n個點,只需找出n-1條邊

        find();

        while(cmp(tempi, tempj)){

            dis[tempi][tempj] = 0;

            find();

        }

        tree[tempi] = 1;

        tree[tempj] = 1;

        dis[tempi][tempj] = 0;//把找過的邊做標記

        sum += min;

    }

    std::cout<<sum;

    return 0;

}

void init(void)

{

    memset(dis, 0, sizeof(dis));

    memset(tree, 0, sizeof(tree));

}

void input(void)

{

    int i, j;

    for(i = 0; i<n; i++)

        for(j = 0; j<n; j++)

            std::cin>>dis[i][j];

}

void find(void)

{

    int i, j;

    min = 1<<30;

    for(i = 0; i<n; i++)

        for(j = 0; j != i; j++)

            if(dis[i][j] != 0 && dis[i][j] < min){

                min = dis[i][j];

                tempi = i;

                tempj = j;

            }

}

int cmp(int i, int j)

{

    if(tree[i] == 1 && tree[j] == 1)

        return 1;

    return 0;

}

 

 

10. Poj2250最長公共子序列

 

poj2250

最長公共子序列 POJ 2250

 

 

Compromise

Time Limit: 1000MS   Memory Limit: 65536K

Total Submissions: 3300   Accepted: 1542   Special Judge

Description

In a few months the European Currency Union will become a reality. However, to join the club, the Maastricht criteria must be fulfilled, and this is not a trivial task for the countries (maybe except for Luxembourg). To enforce that Germany will fulfill the criteria, our government has so many wonderful options (raise taxes, sell stocks, revalue the gold reserves,...) that it is really hard to choose what to do.

 

Therefore the German government requires a program for the following task:

Two politicians each enter their proposal of what to do. The computer then outputs the longest common subsequence of words that occurs in both proposals. As you can see, this is a totally fair compromise (after all, a common sequence of words is something what both people have in mind).

 

Your country needs this program, so your job is to write it for us.

Input

The input will contain several test cases.

Each test case consists of two texts. Each text is given as a sequence of lower-case words, separated by whitespace, but with no punctuation. Words will be less than 30 characters long. Both texts will contain less than 100 words and will be terminated by a line containing a single '#'.

Input is terminated by end of file.

Output

For each test case, print the longest common subsequence of words occuring in the two texts. If there is more than one such sequence, any one is acceptable. Separate the words by one blank. After the last word, output a newline character.

Sample Input

die einkommen der landwirte

sind fuer die abgeordneten ein buch mit sieben siegeln

um dem abzuhelfen

muessen dringend alle subventionsgesetze verbessert werden

#

die steuern auf vermoegen und einkommen

sollten nach meinung der abgeordneten

nachdruecklich erhoben werden

dazu muessen die kontrollbefugnisse der finanzbehoerden

dringend verbessert werden

#

Sample Output

die einkommen der abgeordneten muessen dringend verbessert werden

 

分析:

這個題最重要的是思路,爲什麼要這樣拆分對比的字符串,爲什麼我也想大問題拆小問題,爲什麼還想不到。

最長子序列的問題,相應的公式

i位字符串與j位字符串相同的時候

lcs(i, j) = lcs(i-1, j-1) + "當前相同字符";

i != j時,

lcs(i, j) = max{lcs(i-1, j), lcs(i, j-1)};

要注意的是 邊界問題。另外,標號和大小也需要兩個數組來表示。連着測試代碼一塊貼上來。

 

#include <iostream>

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

#define DEBUG 1

#define lefttop 1

#define left 2

#define top 3

char charx[101][31];

char chary[101][31];

 

//int relation[101][101];

int flag[101][101];

int relation[101][101];

 

int rever_print(int, int);

void search(int i, int j);

int main()

{

    int i, j;

    memset(charx, 0, sizeof(charx));

        memset(chary, 0, sizeof(chary));

            memset(flag, 0, sizeof(flag));

                memset(relation, 0, sizeof(relation));

    int count1 = 1, count2 = 1;

#ifdef DEBUG

   freopen("I:\\input.txt","r",stdin);

#endif

    

    while (scanf("%s", charx[count1]) && strcmp(charx[count1], "#")){

        count1++;

    }

    while (scanf("%s", chary[count2]) && strcmp(chary[count2], "#")){

        count2++;

    }

    for(i = 1; i<count1; i++)

        for(j = 1; j<count2; j++)

            search(i, j);

    for(i = 1; i<count1; i++)

        std::cout<< i<<":"<< charx[i]<<" ";   

    for(j = 1; j<count2; j++){

        std::cout<< j<<":"<< chary[j]<<" ";

    }

    std::cout<<std::endl;

       for(i = 1; i<count1; i++){

         for(j = 1; j<count2; j++)

            std::cout<<flag[i][j]<<"  ";

         std::cout<<std::endl;

    }

    rever_print(count1 - 1, count2 - 1);

    return 0;

}

 

//這個題更適合用循環,嵌套會有大量的重複計算,需要做標記,

//而循環只需要i*j次就好,不需要標記。

//嵌套的優點是,代碼特別短。

void search(int i, int j)

{

    int cmp;

 

    cmp = strcmp(charx[i], chary[j]);

    if(cmp == 0 ){

        relation[i][j] = relation[i-1][j-1] + 1;

        flag[i][j] = 1;//表示斜着過來的

    }

    else if(relation[i-1][j] > relation[i][j-1]){

        relation[i][j] = relation[i-1][j];

        flag[i][j] = 2;//從上面過來的

    }

    else{

        relation[i][j] = relation[i][j-1];

        flag[i][j] = 3;//從左面過來的,用個define更合適

    }

}

int rever_print(int tempi, int tempj)

{

    

    if(!tempi || !tempj)

        return 0;

    if(relation[tempi][tempj] && flag[tempi][tempj] == 1){

        rever_print(tempi-1, tempj-1);

        printf("%s ", charx[tempi]);

    }

    else if(relation[tempi][tempj] && flag[tempi][tempj] == 2 )

        rever_print(tempi-1, tempj);

    else if(relation[tempi][tempj] && flag[tempi][tempj] == 3 )

        rever_print(tempi, tempj-1);

    return 0;

}

 

 

 

11. poj1080Human Gene Funcions

poj1080

基因匹配度計算 poj 1080

 

Human Gene Functions

Time Limit: 1000MS   Memory Limit: 10000K

Total Submissions: 10338   Accepted: 5718

Description

It is well known that a human gene can be considered as a sequence, consisting of four nucleotides, which are simply denoted by four letters, A, C, G, and T. Biologists have been interested in identifying human genes and determining their functions, because these can be used to diagnose human diseases and to design new drugs for them.

 

A human gene can be identified through a series of time-consuming biological experiments, often with the help of computer programs. Once a sequence of a gene is obtained, the next job is to determine its function.

One of the methods for biologists to use in determining the function of a new gene sequence that they have just identified is to search a database with the new gene as a query. The database to be searched stores many gene sequences and their functions – many researchers have been submitting their genes and functions to the database and the database is freely accessible through the Internet.

 

A database search will return a list of gene sequences from the database that are similar to the query gene.

Biologists assume that sequence similarity often implies functional similarity. So, the function of the new gene might be one of the functions that the genes from the list have. To exactly determine which one is the right one another series of biological experiments will be needed.

 

Your job is to make a program that compares two genes and determines their similarity as explained below. Your program may be used as a part of the database search if you can provide an efficient one.

Given two genes AGTGATG and GTTAG, how similar are they? One of the methods to measure the similarity

of two genes is called alignment. In an alignment, spaces are inserted, if necessary, in appropriate positions of

the genes to make them equally long and score the resulting genes according to a scoring matrix.

 

For example, one space is inserted into AGTGATG to result in AGTGAT-G, and three spaces are inserted into GTTAG to result in –GT--TAG. A space is denoted by a minus sign (-). The two genes are now of equal

length. These two strings are aligned:

 

AGTGAT-G

-GT--TAG

 

In this alignment, there are four matches, namely, G in the second position, T in the third, T in the sixth, and G in the eighth. Each pair of aligned characters is assigned a score according to the following scoring matrix.

denotes that a space-space match is not allowed. The score of the alignment above is (-3)+5+5+(-2)+(-3)+5+(-3)+5=9.

 

Of course, many other alignments are possible. One is shown below (a different number of spaces are inserted into different positions):

 

AGTGATG

-GTTA-G

 

This alignment gives a score of (-3)+5+5+(-2)+5+(-1) +5=14. So, this one is better than the previous one. As a matter of fact, this one is optimal since no other alignment can have a higher score. So, it is said that the

similarity of the two genes is 14.

Input

The input consists of T test cases. The number of test cases ) (T is given in the first line of the input file. Each test case consists of two lines: each line contains an integer, the length of a gene, followed by a gene sequence. The length of each gene sequence is at least one and does not exceed 100.

Output

The output should print the similarity of each test case, one per line.

Sample Input

2

7 AGTGATG

5 GTTAG

7 AGCTATT

9 AGCTTTAAA

Sample Output

14

21

 

分析:

類似最長公共子串的一道動態規劃題目。

 

題意

給定兩個基因字符串,用A,C,G,T表示其組成成分。若兩個基因的長度不一樣,可以通過在兩個串中分別添加空格使其長度一致。當其長度一樣後,分別計算對應位置上的兩個字母的分數,並將所有的分數相加便得到兩個串的相似度分數。求,兩個基因串的最高分數。

 

分析

給定兩個基因串Gn, Gm,要求其最高分數f(n, m),有以下兩種情況:

1)G[n] = G[m],則f(n, m) = f(n-1, m-1) + 5;

2)G[n] != G[m], 則可分三種情況:

i.在Gn後添加空格,其分數爲f(n, m) = f(n, m-1) + scores[4][Index(G[m])];

ii. 在Gm後添加空格,其分數爲f(n, m) = f(n-1, m) + scores[Index(G[n])][4];

iii. 直接使用Gn, Gm的最後兩個字配對,其分數爲f(n, m) = f(n-1, m-1) + scores[Index(G[n])][Index(G[m])];

其中,Index()返回字母在分數矩陣中的所在行或列。(詳見原題分數矩陣)

再取i, ii, iii這三種情況的最大值即爲Gn, Gm的最高分數。

 

代碼

 

#include <iostream>  

using namespace std;  

string gen1, gen2;  

int scores[5][5] = {{5, -1, -2, -1, -3}, {-1, 5, -3, -2, -4},  

                    {-2, -3, 5, -2, -2}, {-1, -2, -2, 5, -1},  

                    {-3, -4, -2, -1, 0}};  

int dp[105][105]; //存儲結果  

int getIndex(char c) {  

    if(c == 'A')  

        return 0;  

    else if(c == 'C')  

        return 1;  

    else if(c == 'G')  

        return 2;  

    else if(c == 'T')  

        return 3;  

    else  

        return 4;  

}  

int myMax(int a, int b, int c) {  

    return max(a, max(b, c));  

}  

/*核心函數*/  

int f(int len1, int len2) {  

    if(len1 == 0 && len2 == 0)  

        return 0;  

    char char1, char2;  

    int index1, index2;  

    /*傳統的LCS不用如此初始化,但本題不同,e.g.,f(0,3) != 0*/  

    for(int i = 1; i <= len1; i++) {  

        char1 = gen1[i-1];  

        index1 = getIndex(char1);  

        dp[i][0] = dp[i-1][0] + scores[index1][4];  

    }

    for(int i = 1; i <= len2; i++) {  

        char2 = gen2[i-1];  

        index2 = getIndex(char2);  

        dp[0][i] = dp[0][i-1] + scores[4][index2];  

    }  

    /*DP過程*/  

    for(int i = 1; i <= len1; i++) {  

        for(int j = 1; j <= len2; j++) {  

            char1 = gen1[i-1];  

            char2 = gen2[j-1];  

            if(char1 == char2)  

                 dp[i][j] = dp[i-1][j-1] + 5;  

            else {  

                index1 = getIndex(char1);  

                index2 = getIndex(char2);  

                int sim1, sim2, sim3;  

                sim1 = dp[i-1][j] + scores[index1][4];  

                sim2 = dp[i][j-1] + scores[4][index2];  

                sim3 = dp[i-1][j-1] + scores[index1][index2];  

                dp[i][j] = myMax(sim1, sim2, sim3);  

            }  

        }  

    }  

    return dp[len1][len2];  

}  

int main()  

{  

    int nCases;  

    cin >> nCases;  

    for(int i = 0; i < nCases; i++) {  

        int len1, len2;  

        cin >> len1 >> gen1;  

        cin >> len2 >> gen2;  

        cout << f(len1, len2) << endl;  

    }  

    return 0;  

}

 

 

 

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<iostream>

using namespace std;

//by mars_ch

int t,len1,len2;

char s1[105],s2[105];

int dp[105][105];

int score[5][5]={{5,-1,-2,-1,-3},{-1,5,-3,-2,-4},{-2,-3,5,-2,-2},{-1,-2,-2,5,-1},{-3,-4,-2,-1,0}};

int rr(char c)

{

    if(c == 'A') return 0;

    else if(c == 'C') return 1;

    else if(c == 'G') return 2;

    else if(c == 'T') return 3;

    else return 4;

}

int main()

{

    scanf("%d",&t);

    while(t--)

    {

        memset(dp,0,sizeof(dp));

        scanf("%d %s%d %s",&len1,s1,&len2,s2);

        for(int i=1;i<=len1;i++)

        {

            dp[i][0]=dp[i-1][0]+score[rr(s1[i-1])][4];

        }

        for(int i=1;i<=len2;i++)

        {

            dp[0][i]=dp[0][i-1]+score[4][rr(s2[i-1])];

        }

    //  cout<<"lalala"<<endl;

        for(int i=1;i<=len1;i++)

        {

            for(int j=1;j<=len2;j++)

            {

                if(s1[i-1] == s2[j-1]) dp[i][j]=dp[i-1][j-1]+5;

                else

                {

                    dp[i][j]=max(dp[i][j-1]+score[4][rr(s2[j-1])],max(dp[i-1][j]+score[rr(s1[i-1])][4],dp[i-1][j-1]+score[rr(s1[i-1])][rr(s2[j-1])]));

                }

            }

        }

        printf("%d\n",dp[len1][len2]);

    }
    return 0;}



 

12. poj1458最長公共子序列

Common Subsequence

Description

 

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, ..., xm > another sequence Z = < z1, z2, ..., zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, ..., ik > of indices of X such that for all j = 1,2,...,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input

 

The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output

 

For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input

 

abcfbc         abfcab

programming    contest

abcd           mnp

Sample Output

 

4

2

0

Source

分析:

    最長公共子序列理解匹配公式推導就夠了

    當前匹配時:ch(m, n) = ch(m-1) + ch(n - 1)

     當前不匹配時:ch(m, n) = max(ch(m-1, n), ch(m, n-1)),當然將max換爲min變成了另類的最短子序列查找。

    另外如果需要打印出字符,需要對字符串完成匹配之後才能打印,因此除dp矩陣外還要有一個矩陣來存儲每個匹配字符的傳遞方向。

#include <iostream>

#include <stdio.h>

#include <stdlib.h>

//#define DEBUG 1

//#define INPUT 1

#define MAXNUM 101

unsigned int dp[MAXNUM][MAXNUM];

char sequen1[MAXNUM];

char sequen2[MAXNUM];

int main()

{

    using namespace std;

    int i, j = 1;

#ifdef INPUT

    freopen("I:\\66.txt","r",stdin);

#endif

    std::cout<<"start input:"<<std::endl;

    while(scanf("%s %s", sequen1,sequen2) != EOF){

        memset(dp, 0, sizeof(dp));

#ifdef DEBUG       

        for(i=1; i<=strlen(sequen2); i++)

            cout<<' '<<sequen2[i-1]<<' ';

#endif        

        for(i=1; i<=strlen(sequen1); i++){

#ifdef DEBUG

            cout<<endl<<sequen1[i-1];

#endif            

            for(j=1; j<=strlen(sequen2); j++){

                if(sequen1[i-1] == sequen2[j-1])

                    dp[i][j] = dp[i-1][j-1] + 1;

                else

                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);

#ifdef DEBUG

                cout<<dp[i][j]<<"  ";            

#endif            

            }

        }

        cout<<endl;

    }

    return 0;

}

int max(int a, int b)

{

    return a >= b?a:b;

}

13. poj1159Palindrome

Palindrome

Time Limit: 3000MS   Memory Limit: 65536K

Total Submissions: 33120   Accepted: 11122

Description

A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a program which, given a string, determines the minimal number of characters to be inserted into the string in order to obtain a palindrome.

 

As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome.

Input

Your program is to read from standard input. The first line contains one integer: the length of the input string N, 3 <= N <= 5000. The second line contains one string with length N. The string is formed from uppercase letters from 'A' to 'Z', lowercase letters from 'a' to 'z' and digits from '0' to '9'. Uppercase and lowercase letters are to be considered distinct.

Output

Your program is to write to standard output. The first line contains one integer, which is the desired minimal number.

Sample Input

5

Ab3bd

Sample Output

2

 

分析:

  比較基礎的動態規劃問題,當前字符串可以分解成子串問題,因爲結果只要求輸出個數,所以存儲矩陣都不需要,直接遞歸。

如下:

#include <iostream>

#include <string>

using namespace std;

 

string s;

unsigned short dp(int i, int j);

int main()

{

    int len;

    cin>>len>>s;//輸入

    cout << dp(0, len-1)<< endl;//調用輸出

return 0;

}

unsigned short dp(int i, int j)

{

    if(i>=j)

        return 0;//遞歸到相遇就停止

    if(s[i] == s[j])//處理相同與不同

        return dp(i+1, j-1);

    else

        return min(dp(i+1, j), dp(i, j-1)) + 1;

}

int min(unsigned short a,unsigned short b)

{

    return a>b?b:a;

}

關於用矩陣存儲和使用循環的DP實現,因爲涉及到先後的問題,i,j 兩個位置的距離需要從間隔1開始增加到最大。格式大致如下:

for(dis距離由1增加到len-1)//dis爲i,j的間隔

for(i開始步進,到len-dis)

j=i+dis;//兩個間隔同步,類似於定長窗口移動。




14. poj1753Filp Game

Flip Game

Description

 

Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it's black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules:

Choose any one of the 16 pieces.

Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any).

 

Consider the following position as an example:

 

bwbw

wwww

bbwb

bwwb

Here "b" denotes pieces lying their black side up and "w" denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become:

 

bwbw

bwww

wwwb

wwwb

The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal.

Input

 

The input consists of 4 lines with 4 ch

Output

 

Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it's impossible to achieve the goal, then write the word "Impossible" (without quotes).

Sample Input

 

bwwb

bbwb

bwwb

bwww

Sample Output

 

 

分析:

    方法1:處理少量數據,且不輸出狀態。在這裏關於DFS的遞歸改了好幾遍,主要是對遞歸的次序理不清楚,對比以前寫的遞歸函數,發現是在一開始沒有對DFS函數的意義做明確的定義,導致在遞歸時理不清計算的順序。

    此題用枚舉,按步長找出最少的。

    方法2:處理大量數據。另一種思路,即是從全0或全1狀態出發,列出所有反轉的狀態(狀態與次序無關),將狀態存到表中,後面處理就是對錶的查詢。另外16個bit便可以存儲一個一個棋盤的狀態,再加16bit表示反轉的棋子(反轉與次序無關),共4個字節來表示所有的狀態,2^16個狀態,重合的狀態歸爲一種,但實際能通過反轉達到的共4096種有效。

    下面貼上代碼(這裏是第一種方法):

狀態搜索的次序如下樹:

                                               1                        2        ……         15            16

           /     \  \\\           /     \\\\                 |

                        2           3    …16    3        4…16     16

                                    / | \\\     /   \            ……

  3    4…16 4      5…16

 

           ……

      /         \

   14          15 ……

                  /   \          |

15    16       16 ……

/

16

上圖共4096個分支,從1下最長開始到最後一個16,下面代碼中DFS函數裏的順序就是這樣的

#include<stdio.h>

#define SIZE 4

#define INPUT 1

char field[SIZE][SIZE];

int MIN = 16;

int depth = 16;

const int direction[5][2] = {{-1,0},{1,0},{0,-1},{0,1},{0,0}};//方向,上下左右中

int DFS(int i, int j, int step);

void flip(int i, int j);

int achieve(void);

int main(void)

{

    int ch;

    int i,j;

#ifdef INPUT

    freopen("I:\66.txt","r",stdin);

#endif

        for(i=0;i<SIZE;i++)

        for(j=0;j<SIZE;j++){

        while((ch=getchar())=='\n');

            field[i][j] = (ch == 'b')?0 :1 ;

        }

        for(i=0;i<SIZE;i++)

            for(j=0;j<SIZE;j++){

          //  printf("%d",field[i][j]);

            }

        DFS(0,0,0);

        printf("\n%d",MIN);

        return 0;

}

void flip(int i, int j)

{   

    int h,v,k;

    for(k=0; k<5; k++){

        h = i + direction[k][0];

        v = j + direction[k][1];

        if(h>=0 && h<=SIZE-1 && v>=0 && v<=SIZE-1)

            field[h][v] = !field[h][v];

    }

}

int achieve(void)

{

    int i,j;

    for(i=0;i<SIZE;i++)

        for(j=0;j<SIZE;j++)

            if(field[i][j] != field[0][0])

                return 0;

    return 1;

}

 

//用的深度搜索,存在重複計算

int DFS(int i, int j, int step)

{

    //事實上,step>8即可,最多八步

    if(step>depth||i==4)

        return 0;

    if(achieve())

       return MIN = (MIN>step)?step:MIN;

    flip(i,j);

    if(j<SIZE-1){//加深

        DFS(i, j+1, step+1);

    }

    else{

        DFS(i+1, 0, step+1);

    }

    //橫向搜索

    flip(i,j);

    if(j<SIZE-1){

        DFS(i, j+1, step);

    }

    else {

        DFS(i+1, 0, step);    

    }

    return 0;

}

 

如果需要減少搜索次數

main中

DFS(0,0,0);

改爲

for(depth = 1; depth <16; depth++)

DFS(0,0,0);

這種類似於LFS,每次最多搜索到depth深度的樹枝,可以最快搜出,但存在較多重複計算(比DFS多了很多)。

 

優化方法,必須能保存之前的計算結果,按存儲思路就變成方法二了,不作贅述。

 

15. poj2965ThePilotsBrothers'refrigerator

The Pilots Brothers' refrigerator

Time Limit: 1000MS Memory Limit: 65536K

Total Submissions: 28087 Accepted: 10883 Special Judge

Description

 

The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to open a refrigerator.

 

There are 16 handles on the refrigerator door. Every handle can be in one of two states: open or closed. The refrigerator is open only when all handles are open. The handles are represented as a matrix 4х4. You can change the state of a handle in any location [i, j] (1 ≤ i, j ≤ 4). However, this also changes states of all handles in row i and all handles in column j.

 

The task is to determine the minimum number of handle switching necessary to open the refrigerator.

 

Input

 

The input contains four lines. Each of the four lines contains four characters describing the initial state of appropriate handles. A symbol “+” means that the handle is in closed state, whereas the symbol “?” means “open”. At least one of the handles is initially closed.

 

Output

 

The first line of the input contains N – the minimum number of switching. The rest N lines describe switching sequence. Each of the lines contains a row number and a column number of the matrix separated by one or more spaces. If there are several solutions, you may give any one of them.

 

Sample Input

 

-+--

----

----

-+--

Sample Output

 

6

1 1

1 3

1 4

4 1

4 3

4 4

Source

 

Northeastern Europe 2004, Western Subregion

 

分析:

類似於filp那道題,只不過這個要求打印步數和路徑,路徑一般採用存儲的方式。

但這個不用遍歷,不用搜索。

tips:對於每個需要改變的點,對該點,以及該點行列上所有的點進行操作,可以在僅修改該點狀態的情況下,棋盤上其他的點狀態不變。

因此,以題中給的例子爲例:

需要修改的爲 1,2)和(4,2)位置的點,又tips可知,(1,2)和(4,2)處的點也需要進行操作。由於兩個點周圍,如(3,2)、(2,2)需要進行兩次反轉操作,可視爲沒有進行操作,故以一個4*4矩陣對每個點需要進行的操作次數計數,,例子的計數應如下:

1 2 1 1

0 2 0 0

0 2 0 0

1 2 1 1

其中,反轉兩次和不反轉一樣

1 0 1 1

0 0 0 0

0 0 0 0

1 0 1 1

故需要反轉6個點,(1 1)(1 3)(1 4)(4 1)(4 3)(4 4)。                      


   

#include <iostream>

#define SIZE 4

#define INPUT 1

char field[SIZE][SIZE];

char record[SIZE][SIZE];

int MIN = 16;

void flip(int i, int j);

 

int main(void)

{

    using namespace std;

    int ch;

    int i,j;

#ifdef INPUT

    freopen("I:\\66.txt","r",stdin);

#endif

        for(i=0;i<SIZE;i++)

            for(j=0;j<SIZE;j++){

                while((ch=getchar())=='\n');

                    field[i][j] = (ch == '+')?0 :1 ;

                     record[i][j] =  0;

            }

        for(i=0;i<SIZE;i++)

            for(j=0;j<SIZE;j++)

              if(field[i][j]==0)

                  flip(i, j);

        for(i=0;i<SIZE;i++)

            for(j=0;j<SIZE;j++)

                if(record[i][j]==1){

                    cout<<i+1<<" "<<j+1;

                    cout<<endl;

                }

        return 0;

}

 

void flip(int i, int j)

{   

    int h;

    for(h=0; h<4; h++){

        record[i][h] = !record[i][h];

        record[h][j] = !record[h][j];

    }

    record[i][j] = !record[i][j];

}                

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章