以前在OJ上做的题目(1)

    以前在OJ上做的一些题目,总结了一下,写成结题报告,既是给自己的一个总结,也为其他想要参考的ACMer提供一个方便。

POJ 2192 Zipper

    做这个题目,我是不记得交了多少次,只记得倒数第二次交的时候是个PE,因为我在最后一个输出语句的set d后面加了一个空格才打冒号。这说明什么嘞?说明我真的好粗心,我想是这样的。

    题目是我以前做过的,那个时候用的是比较呆滞的方法,结果没有做出来,应该说,那个时候在交过两次之后,我自己已经发现了问题的所在,但因为那种方法的限 制,我没有能力对它再进行修改。所以,现在重新做的时候,是把以前写的那个全删了再重写的。当时是直接把第三个与第一个与第二个进行匹配,但其中有个问题 就是如果两个字符串如:cateb,cabte,那么程序在有两个或者更多个相同的字符的情况下,只能对其前一个重新判断,也就是说像这种有两个相同但又 要取后者的话,就无能为力了。面对这个问题,我也尝试着用回溯做过,但好像并没有起什么作用,最后还是WA了。现在用的这种方法不晓得叫什么名字好,虽然 这是个DP题目,但我还是觉得这种方法不算DP,最多就是说借用了几个数字跟DP里那个保存状态可以扯上一点点关系。大概的思路是这样的:将第三个字符串 与第一个跟第二个字符串进行匹配,如果只有一个能够匹配上,就让这个匹配,如果两个都能够匹配,那就把它们放入栈中,而取第一个进行匹配,等到以后不能匹 配的时候,就说明当时取第一个进行匹配是错误的,然后就把它们从栈中重新取出,跟第二个进行匹配。这个时候如果栈为空,刚说明第三个不能够跟第一个和第二 个的组合成功匹配,输出“no”的信息,退出,进行下次的输入。

代码如下:

#include <iostream>
#include <string>
using namespace std;

char first[201], second[201], third[401];
int fistack[200], sestack[200], thstack[200], top;
int main()
{
	int n, num = 1;
	cin>>n;
	while(num <= n){
		//		cin>>first>>second>>third;
		scanf("%s%s%s", first, second, third);
		int filen = strlen(first),	selen = strlen(second), thlen = strlen(third);
		top = 0;
		bool flag = true;
		//本来是没有这个操作的,但交了两次之后发现超时了,然后就只能在这里进行一个剪枝了
		for(int i = 0; i < filen; i++)
			fistack[first[i]-'A']++;
		for(int i = 0; i < selen; i++)
			sestack[second[i]-'A']++;
		for(int i = 0; i < thlen; i++)
			thstack[third[i]-'A']++;
		for(int i = 0; i < 26; i++){
			if(fistack[i]+sestack[i] != thstack[i]){
				printf("Data set %d: no\n", num++);				
				flag = false;
				break;
			}
			if(fistack[i+32]+sestack[i+32] != thstack[i+32]){
				printf("Data set %d: no\n", num++);
				flag = false;
				break;
			}
		}
		int i = 0, j = 0, k = 0;
		while(flag && (k < thlen)){
			if((first[i] == third[k]) && (second[j] == third[k])){
				fistack[++top] = i;
				sestack[top] = j;
				i++;	k++;	//两个相同时,默认让第一个与第三个进行匹配
				continue;
			}
			if((first[i] == third[k]) && (second[j] != third[k])){
				i++;	k++;
				continue;
			} 
			if((first[i] != third[k]) && (second[j] == third[k])){
				j++;	k++;
				continue;
			}
			if(top > 0){
				i = fistack[top];
				j = sestack[top]+1;	//这时说明默认的第一个与第三个匹配不成功,因而改为让第二个与第三个匹配
				k = i+j;
				top--;
			}
			else{ 
				printf("Data set %d: no\n", num++);
				//	cout<<"Data set "<<num++<<" : no"<<endl;
				flag = false;
				break;
			}

		}
		if(flag)
			printf("Data set %d: yes\n", num++);
		//			cout<<"Data set "<<num++<<" : yes"<<endl;
		memset(fistack, 0, sizeof(fistack));
		memset(sestack, 0, sizeof(sestack));
		memset(thstack, 0, sizeof(thstack));
	}
	return 0;
}



 


<!---->#include  < iostream >
#include 
< string >
using   namespace  std;

char  first[ 201 ], second[ 201 ], third[ 401 ];
int  fistack[ 200 ], sestack[ 200 ], thstack[ 200 ], top;
int  main()
{
    
int  n, num  =   1 ;
    cin
>> n;
    
while (num  <=  n){
        
//         cin>>first>>second>>third;
        scanf( " %s%s%s " , first, second, third);
        
int  filen  =  strlen(first),    selen  =  strlen(second), thlen  =  strlen(third);
        top 
=   0 ;
        
bool  flag  =   true ;
        
// 本来是没有这个操作的,但交了两次之后发现超时了,然后就只能在这里进行一个剪枝了
         for ( int  i  =   0 ; i  <  filen; i ++ )
            fistack[first[i]
- ' A ' ] ++ ;
        
for ( int  i  =   0 ; i  <  selen; i ++ )
            sestack[second[i]
- ' A ' ] ++ ;
        
for ( int  i  =   0 ; i  <  thlen; i ++ )
            thstack[third[i]
- ' A ' ] ++ ;
        
for ( int  i  =   0 ; i  <   26 ; i ++ ){
            
if (fistack[i] + sestack[i]  !=  thstack[i]){
                printf(
" Data set %d: no\n " , num ++ );               
                flag 
=   false ;
                
break ;
            }
            
if (fistack[i + 32 ] + sestack[i + 32 !=  thstack[i + 32 ]){
                printf(
" Data set %d: no\n " , num ++ );
                flag 
=   false ;
                
break ;
            }
        }
        
int  i  =   0 , j  =   0 , k  =   0 ;
        
while (flag  &&  (k  <  thlen)){
            
if ((first[i]  ==  third[k])  &&  (second[j]  ==  third[k])){
                fistack[
++ top]  =  i;
                sestack[top] 
=  j;
                i
++ ;    k ++ ;     // 两个相同时,默认让第一个与第三个进行匹配
                 continue ;
            }
            
if ((first[i]  ==  third[k])  &&  (second[j]  !=  third[k])){
                i
++ ;    k ++ ;
                
continue ;
            }
            
if ((first[i]  !=  third[k])  &&  (second[j]  ==  third[k])){
                j
++ ;    k ++ ;
                
continue ;
            }
            
if (top  >   0 ){
                i 
=  fistack[top];
                j 
=  sestack[top] + 1 ;     // 这时说明默认的第一个与第三个匹配不成功,因而改为让第二个与第三个匹配
                k  =  i + j;
                top
-- ;
            }
            
else {
                printf(
" Data set %d: no\n " , num ++ );
                
//     cout<<"Data set "<<num++<<" : no"<<endl;
                flag  =   false ;
                
break ;
            }

        }
        
if (flag)
            printf(
" Data set %d: yes\n " , num ++ );
        
//             cout<<"Data set "<<num++<<" : yes"<<endl;
        memset(fistack,  0 sizeof (fistack));
        memset(sestack, 
0 sizeof (sestack));
        memset(thstack, 
0 sizeof (thstack));
    }
    
return   0 ;
}

POJ 2479 Maximum sum

    应该说这是一个很纯正的动态规划,开始的时候我是用的O(N^2)的动态规划做的,结果很显然地超时了。现在想想,那个时候怎么会这样去想嘞,数据给出的 就是50000,O(N^2)的算法应该是很显然会超时的,但无论怎样,我还是对这个题目做得蛮有感觉的,毕竟那种O(N^2)的算法完全是凭借自己的想 法做出来得,也算对自己的一点点安慰吧。O(N^2)的算法思路比较明显,在外层循环计算从左到右的最大值的时候,用内层循环来计算从右到左的最大值,其 中求最大值的步骤就用DP的思想。O(N^2)的代码如下:

 

#include <iostream>
using namespace std;

int ans[50001], num[50001], tmp[50001];
int main()
{
	int tcase, n;
	scanf("%d", &tcase);
	int i, j, sum, max;
	while(tcase--)
	{
		scanf("%d", &n);
		for(i = 1; i <= n; i++)
			scanf("%d", &num[i]);
		ans[0] = 0;
		sum = -999999999;
		for(i = 1; i < n; i++)
		{
			if(ans[i-1] > 0)
				ans[i] = ans[i-1]+num[i];
			else
				ans[i] = num[i];
			tmp[i] = 0;
			max = -999999999;
			for(j = i+1; j <= n; j++)
			{
				if(tmp[j-1] > 0)
					tmp[j] = tmp[j-1]+num[j];	
				else
					tmp[j] = num[j];
				if(max < tmp[j])
					max = tmp[j];
			}
//			cout<<"ans[i] is "<<ans[i]<<" max is "<<max<<endl;
			if(ans[i]+max > sum)
				sum = ans[i]+max;
//			cout<<"sum is "<<sum<<endl;
		}
		cout<<sum<<endl;
	}	
	return 0;
}



 

    后来,这样超时了,于是寻找O(N)的动规,最后得出的方法就是:在输入的时候,进行一次DP,求从左到右的最大值,再一次for循环,从右到左求最大值,同时不断更新最后要求的结果,等到第二次的for循环结束的时候,要求的最大值也就出来了。

O(N)的DP代码如下:

#include <iostream>
using namespace std;

int array[50001], num[50001];
const int MIN = -999999999;

int main()
{
    int tcase, n;
    cin>>tcase;
    int tmp, ans, i, sum;
    while(tcase--){
        scanf("%d", &n);
        tmp = MIN;	sum = 0;
        for(i = 1; i <= n; i++){
            scanf("%d", &num[i]);
            sum += num[i];
            if(sum > tmp)
                tmp = sum;
            array[i] = tmp;
            if(sum < 0)
                sum = 0;
        }
        tmp = ans = MIN;
        sum = 0;
        for(i = n; i > 1; i--){
            sum += num[i];
            if(sum > tmp)
                tmp = sum;
            if(ans < (array[i-1]+tmp))
                ans = array[i-1]+tmp;
            if(sum < 0)
                sum = 0;	
        }			
        cout<<ans<<endl;
    }	
    return 0;
}



 

TOJ 3025 Once Around the Lock

    当时我跟我的另外一个队友都在考虑这个题目,本来是他在做的,后面我做完我的之后,发现已经4点40了,我以为是5点结 束,就没想再做了。于是帮他一起想测试数据,结果弄到5点多很多了,发现大家还在做,才知道是6点结束,于是自己也开始真正对这个题目进行思考。由于他给 我大概地讲了一下题目的意思,我也就只是把整个题目看了一下,没有仔细思考题目的意思,结果就因为这个让我们都犯了严重的错误,对题目的理解没到位,后面 怎么做都不对。在听了别人的解释之后,我把自己之前写的全部删掉,重新做了一次。

 

题目应该可以这样理解:要寻找三次组合使转动后的结果是两个位置相同,但可以相互重合,也就是说可以到了第二步,然后发现这一步行不通了,但符合第一步的 条件,这个时候并不能判断就是Closed了(我们之前都这样想的),它可以又从第一步开始进行整个过程,也就是结束唯一条件就是“?“,只要在这个之前 刚好是连续的C、CC、C但三个转动后位置与给定的位置相同就可以了。当然,这其中的C和CC都可以是一连串的C或者CC组合,只是不能相互杂合在一起。

下面我的代码:

#include <iostream>
#include <string>
using namespace std;

int main()
{
	int n, x, y, z, k = 1, turn, //一次输入的移动数
		sum, top, times;
	bool last;
	char flag[3];
	while(cin>>n){
		if(!n)
			break;
		cin>>x>>y>>z;
		//init
		sum = 0;	top = 0;	times = 0;	last = 0;
		while(cin>>flag){
			if(flag[0] == '?')
				break;
			if(!strcmp(flag, "C")){	// C turn
				cin>>turn;
				if(!last){	//last turn is CC
					last = true;
					top -= turn;
					while(top < 0)
						top += n;
					sum = turn;
					if((times == 2) && (top == z) && (sum <= n))
						times = 3;
					if((times == 2) && (sum > n))
						times = 0;
					if((times == 0) && (top == x) && (sum >= n))
						times = 1;
					if((times == 2) && (top == x) && (sum >= n))
						times = 1;
					if((times == 1) && (top != x))
						times = 0;
					if((times == 1) && (sum < n))
						times = 0;
				}
				else{	//last turn is C
					top -= turn;
					while(top < 0)
						top += n;
					sum += turn;
					if((times == 0) && (top == x) && (sum >= n))
						times = 1;
					if((times == 1) && (top != x))
						times = 0;
					if((times == 3) && (top == x) && (sum >= n))
						times = 1;
					if(times == 3)
						times = 0;
					else if((times == 2) && (top == z) && (sum <= n))
						times = 3;
					else if((times == 2) && (top == x) && (sum >= n))
						times = 1;
					else if((times == 2) && (sum >= n))
						times = 0;
				}
			}
			else{	//CC turn
				cin>>turn;
				if(last){	//last turn is C
					top += turn;
					top %= n;
					sum = turn;
					last = false;
					if(times == 2)
						times = 0;
					if((times == 1) && (sum <= 2*n) && (sum > n) && (top == y))
						times = 2;
					if(times == 3)
						times = 0;
				}
				else{	//last turn is CC
					top += turn;
					top %= n;
					sum += turn;
					if(times == 2) 
						times = 0;
					if((times == 1) && (sum <= 2*n) && (sum > n) && (top == y))
						times = 2;
				}
			}
		}
		if(times == 3)
			cout<<"Case "<<k++<<": Open"<<endl;
		else
			cout<<"Case "<<k++<<": Closed"<<endl;
	}
	return 0;
}



 

TOJ 3027 And Now, a Remainder from Our Sponsor

    这是那天比赛我做的另外一个题目,题目的意思就不说了,还是蛮容易看懂得。整个过程也相对比较简单,就是由输入的一个七位 或者八位数跟前面的四个数求一个五位或者六位数,让这个数对那四个数求模得到的数的组合就是输入的数,不过需要注意其中位数不同以及0的问题,最后,不要 把结果中最后的空格输出。TOJ总喜欢干这事,不小心的人就总会得到PE。

 

#include <iostream>
#include <string>
using namespace std;

int keys[4], tmp[4];
char message[50][10];
char ans[152];

int main()
{
	int tcase, n, i;
	cin>>tcase;
	int tmp2, max, maxTag, code, ansTag;
	bool tag;
	while(tcase--){
		scanf("%d", &n);
		max = 0;	maxTag = 0;	
		for(i = 0; i < 4; i++){
			scanf("%d", &keys[i]);
			if(keys[i] > max){
				max = keys[i];
				maxTag = i;
			}
		}
		ansTag = 0;
		for(i = 0; i < n; i++){
			tag = true;
			scanf("%s", message[i]);
			if(strlen(message[i]) == 7)
				tag = false;
			if(tag)
				tmp[0] = (message[i][0]-'0')*10+message[i][0+tag]-'0';
			else
				tmp[0] = message[i][0]-'0';
			tmp[1] = (message[i][1+tag]-'0')*10+message[i][2+tag]-'0';
			tmp[2] = (message[i][3+tag]-'0')*10+message[i][4+tag]-'0';
			tmp[3] = (message[i][5+tag]-'0')*10+message[i][6+tag]-'0';
			//求code
			for(code = 10000; ; code++){
				if(code%max == tmp[maxTag])
					break;	
			}
			while(true){
				if((code%keys[0]==tmp[0]) && (code%keys[1]==tmp[1]) && (code%keys[2]==tmp[2]) && (code%keys[3]==tmp[3]))
					break;
				code += max;
			}
			tmp2 = code/100000;	code %= 100000;
			tmp2 = tmp2*10+code/10000;	code %= 10000;
			if(tmp2 == 27)	ans[ansTag++] = ' ';
			else	ans[ansTag++] = tmp2-1+'A';
			tmp2 = code/1000;	code %= 1000;
			tmp2 = tmp2*10+code/100;		code %= 100;
			if(tmp2 == 27)	ans[ansTag++] = ' ';
			else	ans[ansTag++] = tmp2-1+'A';
			tmp2 = code/10;	code	%= 10;
			tmp2 = tmp2*10+code;
			if(tmp2 == 27)	ans[ansTag++] = ' ';
			else	ans[ansTag++] = tmp2-1+'A';
		}
		ansTag--;
		while(ans[ansTag] == ' ')
			ansTag--;
		for(i = 0; i <= ansTag; i++)
			printf("%c", ans[i]);
		printf("\n");
	}	
	return 0;
}



 

TOJ 3021 CIVIC DILL MIX

    这个题目是比赛的时候我做的一个题目,当时只求快点把它做完,好去做其他的题目,也就没管太多的时间或者空间效率了,可以优化的地方应该是有很多很多吧!

我当时的想法是:整个程序分为三个模块,一个就是main函数,另外一个就是把一个阿拉伯数字转化成罗马字符串(这个函数就叫做toToman),最后一 个把罗马字符串转化成阿拉伯数字(这个函数叫做toNormal)。toNormal这个功能相对而言要简单很多,直接从左到右遍历一次就好了,只有一种 特殊情况,就是有可能左边的那个数会作为被右边的数减去的数,所以遍历的时候需要考虑一下遍历到的数的右边的数的情况。toRoman的话,我没有想太 多,就直接做了,如果这个数比1000大就不断地进行减1000操作,直到它小于1000,如果还是比900大,那么就转化成CM的形式;else if比500大,就从中减掉一个500;else if比400大,就要化成CD的形式。再重复上面的操作,但把上面的数都除以10。整个过程就相当于对9、5、4这三个特殊的数进行操作的过程。

下面是我写的代码:

#include <iostream>
#include <string>
using namespace std;

char instr[100], outstr[100];
int letter[26] = {0,0,100,500,0,0,0,0,1,0,0,50,1000,0,0,0,0,0,0,0,0,5,0,10,0,0};
int toNormal()
{
	int i = 0, length = strlen(instr), ans = 0;
	if(length == 1)
	{
		ans = letter[instr[0]-'A'];
		return ans;
	}
	while(i < length-1){
		if(letter[instr[i]-'A'] < letter[instr[i+1]-'A']){
			ans -= letter[instr[i]-'A'];	
		}	
		else 
			ans += letter[instr[i]-'A'];
		i++;
	}
	ans += letter[instr[length-1]-'A'];
	return ans;
}

void toRoman(int n)
{
	int tag = 0;
	while(n >= 1000){
		outstr[tag++] = 'M';
		n -= 1000;	
	}
	if(n >= 900){
		outstr[tag++] = 'C';	outstr[tag++] = 'M';
		n -= 900;	
	}
	else if(n >= 500){
		outstr[tag++] = 'D';	n -= 500;
	}
	else if(n >= 400){
		outstr[tag++] = 'C';	outstr[tag++] = 'D';
		n -= 400;	
	}
	
	while(n >= 100){
		outstr[tag++] = 'C';	n -= 100;	
	}	
	if(n >= 90){
		outstr[tag++] = 'X';	outstr[tag++] = 'C';
		n -= 90;
	}
	else if(n >= 50){
		outstr[tag++] = 'L';	n -= 50;	
	}
	else if(n >= 40){
		outstr[tag++] = 'X';	outstr[tag++] = 'L';
		n -= 40;	
	}
		
	while(n >= 10){
		outstr[tag++] = 'X';	n -= 10;	
	}
	if(n == 9){
		outstr[tag++] = 'I';	outstr[tag++] = 'X';	
		n -= 9;
	}
	else if(n >= 5){
		outstr[tag++] = 'V';	n -= 5;	
	}
	else if(n == 4){
		outstr[tag++] = 'I';	outstr[tag++] = 'V';
		n -= 4;	
	}
	while(n >= 1){
		outstr[tag++] = 'I';	n--;	
	}
	outstr[tag] = '\0';
}

int main()
{
	int n, k = 1, sum, i;
	while(scanf("%d", &n) != EOF){
		if(!n)
			break;
		sum = 0;
		for(i = 0; i < n; i++){
			scanf("%s", instr);	
			sum += toNormal();
		}
		toRoman(k);
		printf("Case %s: ", outstr);
		toRoman(sum);
		printf("%s\n", outstr); 
		k++;
	}	
	return 0;
}



 

POJ 3505 Tower Parking (TOJ 3037)

    可能是我的英语水平太有限了吧,这个题目我硬是看了好久然后别人跟我大概地说了下,我才把题意弄懂。就是说一个类似于停车 库的东西里面放有许多车子,这个停车库有很多层,每一层又类似于一个转盘,或者说在每一层有一个转盘,可以通过顺时针或者逆时针转动把车子移动到电梯口, 然后把车子运到第一层的出口给取车的人,在整幢停车库里,车子是有编号的(从1到车子的数目N),也就是说取车的时候要先取1,然后取2,最后再取N,求完成整个过程的时间。

    我是这样想的:在输入数据的时候对数据进行一下预处理,如果该位置的编号为-1,也就是说这个地方没有停车,就不管它;如果该位置有不为-1的编号,假设 编号为M,那么这辆车就要在第M次取车的时候被取走,我就把这辆车初始所在位置记录在下标为M的数组中。这样预处理之后,第一次要被取的车的信息就存放在 数组下标为1的元素中,第M次要被取的车的信息就存放在数组下标为M的元素中。另外再建一个数组用来保存每层顺时针或者逆时针转动的信息,最后取车的时候 被取的车的位置就由这两个数组的信息来唯一确定。

下面是我写的代码:

#include <iostream>
using namespace std;

struct Node{
	int x, y;
}pos[2501];

int turn[50];
int main()
{
	int tcase, height, length;
	cin>>tcase;
	while(tcase--){
		cin>>height>>length;
		int total = 0, tmp;
		for(int i = 0; i < height; i++)
			for(int j = 0; j < length; j++){
				cin>>tmp;
				if(tmp != -1){
					total++;
					pos[tmp].x = j;
					pos[tmp].y = i;
				}
			}
		//end of input
		int k = 1;
		int curX, curY, ans = 0;
		while(k <= total){
			curY = pos[k].y;	curX = pos[k].x+turn[curY];
			if(curX >= length)
				curX %= length;
			while(curX < 0)
				curX += length;
			//cout<<"curX is "<<curX<<" curY is "<<curY<<endl;
			if(curX > length-curX){
				ans += (length-curX)*5+curY*20;
				turn[curY] += length-curX;
			}
			else{
				ans += curX*5+curY*20;
				turn[curY] -= curX;
			}
			k++;
			//cout<<"ans is "<<ans<<endl;
		}
		cout<<ans<<endl;
		memset(turn, 0, sizeof(turn));
	}
	return 0;
}



 

你可能感兴趣的:(C++,c,算法,C#,J#)