UVA - 185 Roman Numerals(回溯+剪枝)

该程序读取罗马数字求解等式正确性及阿拉伯编码可能性。正确罗马数字输出'correct',错误则输出'incorrect'。阿拉伯编码可能唯一输出'valid',多解输出'amiguous',无解输出'impossible'。采用回溯加剪枝策略解决问题。
摘要由CSDN通过智能技术生成


  Roman Numerals 

The original system of writing numbers used by the early Romans was simple but cumbersome. Various letters were used to represent important numbers, and these were then strung together to represent other numbers with the values decreasing monotonically from left to right. The letters they used and the numbers that were represented are given in the following table.

I1 V5
X10 L50
C100 D500
M1000   

Thus 1993 was written as MDCCCCLXXXXIII. This system was then superseded by a partially place-oriented system, whereby if the above rule of decreasing values was broken, it meant that the immediately preceding (lower) value was deemed to be `negative' and was subtracted from the higher (out of place) value. In this system 1993 was usually written as MCMXCIII. There is still some controversy as to which letters could precede which other letters, but for the purposes of this problem we will assume the following restrictions:

1.
A letter from the left column can never appear more than three times in a row, and there can never be more than one other occurrence of that letter.

2.
A letter from the right column can never appear more than once.

3.
Once a letter has been used in a `negative' position, all subsequent characters (apart from the one immediately following) may not be greater than that character.

Thus we could write MXMIII for 1993 or CCXCIV for 294, however we could not write ILV for 54, nor could we write LIL for 99. Note that 299 could be written as CCXCIX or CCIC


Given a Roman sum, we can either interpret it as such or as an encoding of an Arabic sum. Thus V+V=X could be interpreted as an ambiguous encoding of an Arabic sum with V$\in$ {1, 2, 3, 4} and X = 2 * V. Similarly, X+X=XX could be interpreted as a correct Roman sum but an impossible Arabic encoding (apart from the trivial encoding X = 0) and XX+XX=MXC as an incorrect Roman sum, but a valid encoding with M = 1, X = 9, and C = 8.


Write a program that will read in sums in Roman numerals and determine whether or not they are correct as Roman sums and also whether they are impossible, ambiguous or valid as Arabic encodings. Assume that zero will never appear on its own or as a leading digit, and that no two Roman numerals map onto the same Arabic digit.

Input 

Input will consist of a series of lines, each line consisting of an apparent Roman sum, i.e. a valid Roman number, a plus sign (  + ), another valid Roman number, an equal sign (  = ) and another valid Roman number. No Roman number will contain more than 9 letters. The file will be terminated by a line consisting of a single  # .

Output 

Output will consist of a series of lines, one for each line of the input, and each containing two words. The first word will be one of (  Correct, Incorrect ) depending on whether the Roman sum is or is not correct. The second word will be separated from the first by exactly one space and will be one of the set (impossible, ambiguous, valid) depending on the Arabic sum.

Sample input 

V+V=X
X+X=XX
XX+XX=MXC
#

Sample output 

Correct ambiguous
Correct impossible
Incorrect valid


翻译:

原始的罗马数字的书写系统是简单,但是冗长的。各种各样的字母被用来代表重要的数字,把他们捆绑在一起用来表示其他的从左到右单调递减的数字。

罗马数字的字母和对应的数字在下表中给出。

I1 V5
X10 L50
C100 D500
M1000   

因此1993被写作MDCCCCLXXXXIII。
该系统随后被分布导向的制度所取代,如果减少值上述规则被打破,这意味着前一更小的数被认为是'负',并从更大的数中减去。
在这个系统中1993也通常被写作MCMXCIII。
可能还有一些争议,哪个字母可以优先于其他字母,但对于这个问题,我们将按照以下限制:

  1. 一个字母从左边开始不能在一行中连续出现3次,而且永远不会有一个以上的其他单词出现。
  2. 右侧的字母不能出现超过一次。
  3. 一旦一个字母被用到负数的地方,其随后的所有字符(除了一个紧随其后的)可能不会大于这个字母。
因此我们能用MXMIII来表示1993,CCXCIV表示294,然而我们不能用ILV来表示54,也不能用LIL来表示99
现在给一个罗马数字的和,我们既可以把它解释为罗马数字,也可以把它转换为阿拉伯的编码

因此V+V=X能被解释成多种形式的阿拉伯数字的编码为 V $\in${1,2,3,4} 和 X=2×V
类似地,X+X=XX可以被解释为一个正确的罗马总和但不可能阿拉伯编码(除了编码X=0)和
XX+XX=MXC为一个不正确的罗马总和,但一个有效的编码为 M =1,X= 9,C= 8。
编写一个程序,读取罗马数字的和,并确定他们是否正确作为罗马数字的和,和是否是不可能的,或者是模棱两可或者是有效的阿拉伯编码。
假设零永远不会出现在自己本身的或作为一个前缀,没有两个罗马数字映射到相同的阿拉伯数字。


解析:题目其实可以当作两道题来对待。

问题1,将罗马数字转化为阿拉伯数字,判断等式是否正确。如果正确输出correct,否则输出incorrect


例如MCMXCIII 根据题目给出的表

M=1000,C=100,I=1。

那么第一个M=1000,然后C=-100(因为它右边是1000,100<1000),然后第二个M=1000,I是1(右边相等不变负数)

结果就是1000 - 100 + 1000 +1 +1 +1 = 1993

而第一个问题只要转化成阿拉伯数字,再判断等式是否相等,相对简单。


问题2,每个字母可以代表任意的0 - 9 的数,但数字的前缀不能为0,而且相同的数字不能对应不同的字母。

然后判断是否存在一个等式能使两边成立,只存在唯一可能输出valid,如果存在多种可能输出ambiguous,如果不存在输出impossible


例如V+V=X 可以是1+1=2 2+2=4 3+3=6 4+4=8 多种情况,输出ambiguous
而XX+XX=MXC  只能是99+99=198一种情况,就输出ambiguous

思路:回溯加剪枝。首先要把出现过的字母标记为访问,并统计字母的个数,这样只要给这些出现过的字母赋值,

并对每个出现过的字符串的首字母标记为true。


剪枝点:

  1. 如果当前为首字母,且当前将要赋值的数字为0,跳过。
  2. 如果当前cnt>=2,跳出。
  3. 如果当前数字已经被用于赋值,跳过。

#include <stdio.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;
const int N = 256;
const char letter[] = "IXCMVLD";

string str;
string num[3];
int vis[N];
int map[N];
int ans[3];
int map2[N];//映射每个字母所代表的数字
int head[N];//保存每个字母的前缀
int cnt;
int size; //保存单词的总长度

void table() {
	map['I'] = 1;
	map['X'] = 10;
	map['C'] = 100;
	map['M'] = 1000;
	map['V'] = 5;
	map['L'] = 50;
	map['D'] = 500;
}

void init() {
	memset(vis,0,sizeof(vis));
	memset(ans,0,sizeof(ans));
	memset(head,0,sizeof(head));
	memset(map2,0,sizeof(map2));
	for(int i = 0; i < 3; i++) {
		num[i] = "";
	}
	size = 0;
}

int change(string tmp) {
	int sum = 0;
	for(int i = 0; i < tmp.size() - 1; i++) {
		if(map[tmp[i]] >= map[tmp[i+1]]) {
			sum += map[tmp[i]];
		}else {
			sum -= map[tmp[i]];
		}
	}
	sum += map[tmp[tmp.size()-1]];
	return sum;
}

int tran_num(string tmp) {
	int sum = 0;
	for(int i = 0; i < tmp.size(); i++) {
		sum = sum*10 + map2[tmp[i]];
	}
	return sum;
}
void dfs(int cur,int left) {
	if(cur == size) {//出口
		ans[0] = tran_num(num[0]);
		ans[1] = tran_num(num[1]);
		ans[2] = tran_num(num[2]);
		if(ans[0] + ans[1] == ans[2]) {
			cnt++;
		}
		return;
	}
	
	for(int i = left; i < 7; i++) {
		if(vis[letter[i]]) {
			for(int j = 0; j <= 9; j++) {
				if(!vis[j]) {
					if(j == 0 && head[letter[i]]) {
						continue;
					}
					
					vis[j] = true;
					vis[i] = false;
					
					map2[letter[i]] = j;
					dfs(cur+1,i+1);
					
					vis[i] = true;
					vis[j] = false;
					if(cnt >= 2) {
						return;
					}
				}
			}
		}
	}
}

int main() {
	table();
	while(cin >> str) {
		init();
		if(str[0] == '#') {
			break;
		}
		int flag = 0;
		for(int i = 0; i < str.size(); i++) {
			if(str[i] == '+' || str[i] == '=') {
				flag++;
				continue;
			}
			if(!vis[str[i]]) {
				vis[str[i]] = true;
				size++;
			}
			if(flag == 0) {
				num[0] += str[i];
			}else if(flag == 1) {
				num[1] += str[i];
			}else if(flag == 2) {
				num[2] += str[i];
			}
		}

		for(int i = 0; i < 3; i++) {
			head[num[i][0]] = true;
		}
		for(int i = 0; i < 3; i++) {
			ans[i] = change(num[i]);
		}
		if(ans[0] + ans[1] == ans[2]) {
			cout<<"Correct ";
		} else {
			cout<<"Incorrect ";
		}
		cnt = 0;
		dfs(0,0);
		if(!cnt) {
			cout<<"impossible"<<endl;
		}else if(cnt == 1) {
			cout<<"valid"<<endl;
		}else {
			cout<<"ambiguous"<<endl;
		}
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值