天天看点

POJ 2692:假币问题 / POJ 1013:Counterfeit Dollar

POJ 2692:假币问题 / POJ 1013:Counterfeit Dollar

TIPS:up/down 是以天平右臂为准

  • 思路1:

    由于是通过结果反推硬币真假,想到,将每个硬币都先假设为假币(假币分轻重,故一共有

    12*2

    种情况,如:A轻假/重假,B轻假…),带入3次测试结果,看是否成立,若成立该硬币为假币,反之为真;

    - 3种假设不成立的情况:

    情况1.如果天平是even假币存在

    情况2.如果天平是up:轻币在左,重币在右 或 假币不在

    情况3.如果天平是down:轻币在右,重币在左 或 假币不在(OJ里省略了,对结果没影响,但实际上不能省略,见 注1)

  • code1:
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
char Right[3][7];
char Left[3][7];
char result[3][5];

bool IsFake(char c, bool Weight){
	char *l, *r;
	for(int i = 0; i < 3; ++i){
		l = Left[i];
		r = Right[i];
		if(Weight){
		//如果是轻币
			if(result[i][0] == 'e')
				if(strchr(l, c) || strchr(r, c))
				//1.天平even,假币存在
					return false; 
		 	if(result[i][0] == 'u')
		 		if(strchr(l, c))
		 		//2.天平up,轻币在左,或者轻币不在 `|| (strchr(r, c) == NULL && strchr(l, c) == NULL) ` Q:为啥可以省略轻币不在的情况?
		 			return false;
			if(result[i][0] == 'd')
				if(strchr(r, c))
				//3.天平down,轻币在右,或者轻币不在 
					return false; 
		}
		else{
		//如果是重币
			if(result[i][0] == 'e')
				if(strchr(l, c)|| strchr(r, c))
				//1.天平even,假币存在
					return false; 
		 	if(result[i][0] == 'u')
		 		if(strchr(r, c) || (strchr(r, c) == NULL && strchr(l, c) == NULL))
		 		//2.天平up,重币在右 ,或者重币不在
		 			return false;
			if(result[i][0] == 'd')
				if(strchr(l, c) || (strchr(r, c) == NULL && strchr(l, c) == NULL))
				//3.天平down,重币在左 ,或者重币不在
					return false; 	 
		}
	}
	
	return true;
}

int main(){	
	int n;
	cin >> n;
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < 3; ++j){
			cin >> Left[j] >> Right[j] >> result[j];
		}
		for(char c = 'A'; c <= 'L'; ++c){
			if(IsFake(c, true)){
				//如果是轻币
				cout << c << " is the counterfeit coin and it is light.\n"; 
				break;
			}
			if(IsFake(c, false)){
				//如果是重币
				cout << c << " is the counterfeit coin and it is heavy.\n"; 
				break;
			}
		}
	}
	return 0;
}
           
  • code 2(PKU郭炜):
#include <iostream>
#include <cstring>
using namespace std;
char Right[3][7];
char Left[3][7];
char result[3][5];

bool IsFake(char c, bool Weight){
	char *l, *r;
	for(int i = 0; i < 3; ++i){   
		if(Weight){
			l = Left[i];
			r = Right[i];	
		}
		else{	//优化代码:如果是重币,左右天平互换(相当于把重当作轻来看) 
			r = Left[i];
			l = Right[i];
		}
		switch(result[i][0]){
			case 'u':
				if(strchr(r, c) == NULL)
					return false;
				break;
			case 'e':
				if(strchr(l, c) || strchr(r, c))
					return false;
				break;
			case 'd':
				if(strchr(l, c) == NULL)
					return false;
				break;
		} 
	}
	return true;
}

int main(){	
	int n;
	cin >> n;
	while(n--){
		for(int j = 0; j < 3; ++j) cin >> Left[j] >> Right[j] >> result[j];
		for(char c = 'A'; c <= 'L'; ++c){
			if(IsFake(c, true)){
				cout << c << " is the counterfeit coin and it is light.\n"; 
				break;
			}
			else if(IsFake(c, false)){
				cout << c << " is the counterfeit coin and it is heavy.\n"; 
				break;
			}
		}
	}
	return 0;
}
           
  • code3(PKU刘家瑛):
  • 思路2:

    枚举出A~L每个硬币是假币的情况:用1代表重假币,0代表真币,-1代轻假币,计算左右天平各自的重量;

    - 3种假设不成立的情况(比思路1清晰):

    情况1.右侧较沉(左<右),结果却不是down

    情况2.右侧较轻(左>右),结果却不是up

    情况3.一边沉(左=右),结果却不是even

#include <cstdio>

int status[20];
char left[3][7], right[3][7], result[3][5];

bool test(){
	int i, j;
	int SumLeft, SumRight;
	for(i = 0; i <3; ++i){
		SumLeft = 0;
		SumRight = 0;
		for(j = 0; j < 6 && left[i][j] != 0; ++j){
			SumLeft += status[left[i][j] - 'A'];
			SumRight += status[right[i][j] - 'A'];
		}
		if(SumLeft < SumRight && result[i][0] != 'd')
			return false;
		if(SumLeft > SumRight && result[i][0] != 'u')
			return false;
		if(SumLeft == SumRight && result[i][0] != 'e')
			return false;
	}
	return true;
}


int main(){
	int i, n;
	scanf("%d", &n);
	while(n-- > 0){
		for(i = 0; i < 3; ++i)
			scanf("%s %s %s", left[i], right[i], result[i]);
		for(i = 0; i < 12; ++i)
			status[i] = 0;	//0初始化数组,也可以用memset来初始化!
		for(i = 0; i < 12; ++i){
			status[i] = 1;	//假设第i个硬币是重币
			if(test())	//如果证明确实是重币,跳出循环 
				break;
			status[i] = -1;
			if(test())
				break;	
			status[i] = 0;	
		} 
		printf("%c is the counterfeit coin and it is %s.\n", i + 'A', 
				status[i] == 1 ? "heavy" : "light");
	}
	return 0;
}
           
  • 补充知识:

在写C++程序中,总会遇到要从一个字符串中查找一小段子字符串的情况

  • 头文件:
C++:#inlcude<string>
C: #include<string.h>
           

对于在C中,我们经常用到strstr()或者strchr()这两种方法(见 code1)。而对于C++的string,我们往往会用到find()。

find()

在一个字符串中查找一个指定的单个字符或字符数组。如果找到,就返回首次匹配的开始位置;如果没有查找到匹配的内容,就返回

string::npos

find_first_of()

在一个目标串中进行查找,返回值是第一个与指定字符组中任何字符匹配的字符位置。如果没有查找到匹配的内容,则返回

npos

find_last_of()

在一个目标串中进行查找,返回最后一个与指定字符组中任何字符匹配的字符位置。如果没有查找到匹配的内容,则返回

npos

find_first_not_of()

在一个目标串中进行查找,返回第一个与指定字符组中任何字符都不匹配的元素位置。如果找不到那样的元素则返回

npos

find_last_not_of()

在一个目标串中进行查找,返回下标值最大的与指定字符组中任何字符都不匹配的元素的位置。若找不到那样的元素则返回

npos

rfind()

对一个串从尾至头查找一个指定的单个字符或字符组。如果找到,就返回首次匹配的开始位置;如果没有查找到匹配的内容,则返回

npos

find(string, int)

第一个参数用来指示要查找的字符,第二个参数用来表示从字符串的何处开始查找子串(默认的查找位置是0)。

(转载自:https://www.cnblogs.com/sword-/p/8030004.html )

  • 注1:

测试输入:

1
ABCD EFGH even
ABJD EFIH up
CDJA HBKL up
           

理论输出:

实际输出:

  • 注2:

    !!!!!注意

    \n

    ,缺了\n找了半天。
  • 该题同 1013:Counterfeit Dollar
    POJ 2692:假币问题 / POJ 1013:Counterfeit Dollar

继续阅读