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];

}                

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值