codeforces 664B题 Rebus 题解记录了我做这道题从错到对的完整思路

本文介绍了解决一种特殊的算术谜题——Rebus的方法。该谜题要求将未知数替换为1到n之间的整数,使等式成立。文章详细讲解了输入处理和算法实现过程,最终给出了一种有效的解决方案。

B. Rebus
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

      You are given a rebus of form ? + ? - ? + ? = n, consisting of only question marks, separated by arithmetic operation '+' and '-', equality and positive integer n. The goal is to replace each question mark with some positive integer from 1 to n, such that equality holds.

Input

       The only line of the input contains a rebus. It's guaranteed that it contains no more than 100 question marks, integer n is positive and doesn't exceed 1 000 000, all letters and integers are separated by spaces, arithmetic operations are located only between question marks.

Output

       The first line of the output should contain "Possible" (without quotes) if rebus has a solution and "Impossible" (without quotes) otherwise.

       If the answer exists, the second line should contain any valid rebus with question marks replaced by integers from 1 to n. Follow the format given in the samples.

Examples
input
? + ? - ? + ? + ? = 42
output
Possible
9 + 13 - 39 + 28 + 31 = 42
input
? - ? = 1
output
Impossible
input
? = 1000000
output
Possible
1000000 = 1000000

       这是我们今天练习赛的C题,发现我们队中的人在刚开始看完后都表示有很多的困难。先说题意:

        在输入中就是一个算数式,但是,里面所有的数字都用 ‘ ?’ 代替。其中所有‘ ?’的值都必需大于1小于等于输入的n。在输出中要求先输出是否存在某种可能使得所有的‘ ? ’都有一个值使得等式成立。(注意:每一个‘ ?’的值都不能大于输入的n。)如果有可能就输出“Possible”。然后输出每个‘ ?’被代替后的等式。(对于每个输入与其对应的输出不唯一只需输出一个即可。)如果可能就只需输出“ Impossible ”。


      首先,我的队友碰到的最直接的问题就是如何输入。(我们是弱校,真没办法,不过会出现这种问题确实不应该。)还有的就是在分别知道 ‘+’ 和‘-’的数量后应该怎么处理得到每个正数和负数的值。(这个才是这个题的真正难点所在。)

      首先是帮我的队友解决在输入时如何接收 的问题。(在输入的同时就记下了 ‘+’ 和  ‘-’ 的总数。)

下面是代码的框架:


<pre name="code" class="cpp"><pre name="code" class="cpp">

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
char c;
int m[1000000];            //使用数组m来记录‘ + ’,‘ - ’输入的顺序。
int main(){
	int count_num=0;       //使用count_num来记录数字的数量。虽然,最后发现这步是多余的。但是,为了便于理解所以就加上了。
	int count_1=1;         //使用count_1来记录正数的总数,不论是否有符号第一个数字必定是正数所以初始化的时候是1。
                           //(因为,位最后在计算结果时用所有的正数来计算更方便。)
	int count_2=0;         //使用count_2来记录负数的总数。因为每次输入‘ - ’之后再结合下一个输入的‘?’就是一个负数。
	int count=0;
	memset(m,0,sizeof(m)); //在初始化时0表示没有符号。
	while(cin>>c){         //因为在输入数字之前每次输入的只有一个字符(‘-’,‘+’,‘=’或‘?’)。所以,用字符接收。
		if(c=='?'){        //因为每次输入一个问号都表示一个数字所以count_num需自加一(仅有于理解无实际作用可忽略。) 
			count_num++;
		}
		if(c=='+'){                 //每次输入‘ + ’时表示增加了一个正数。count_1++;
			m[count++]=1;           //同时记录这个位置上是‘ + ’。(1表示是‘ + ’,2表示是‘ - ’,count表示的是位置。)
		}
		if(c=='-'){                 //每次输入‘ - ’时增加一个负数。
			count_2++;
			m[count++]=2;           //同时记录这个位置上的是‘ - ’。
		}
		if(c=='='){                //当输入‘ = ’时就可以准备开始计算了。
		int sum;
		cin>>sum;                   //因为在输入‘ = ’接下来输入的就必然是数字n在这里用sum代替。(因为是所有的总和便于理解。)
/***********************************************/
     在此处写入计算的代码。
     还有包括所有的输出的代码也写这里。
	(注意要去掉备注行总能明白吧!)		
/***********************************************/
			count_1=1;             //下面这里都是对原先定义的那些变量进行初始化。
			count_2=0;
			count_num=0;
			count=0;
			memset(m,0,sizeof(m));
		}
	}
}


       花了半天的时间,说明了怎么输入接下来就是说说计算的思路了...............
       我原先的思路是先以为(注意此处的只是我的原先的错误猜想,但是,结果看到了我原先思路上的漏洞。不感兴趣的人可以直接跳过。)让所有的负数都作为 ‘ - 1 ’这样一来原本以为就能给正好更大的变化空间。而不至于,使得正数的值更不容易超过 sum 。
       但是,这样一来所有负数的总和(下面用sum_j代替)是count_2。而所有正数的总和(下面用sum_ad代替)就是sum+sum_j。而最后所以每个正数的最大值是sum*count_1。(也就是当所有的正数都是sum的时候。)当sum+sum_j大于sum*count_1的时候就不符合题意,以及某个正数需要等于‘ 0 ’也不符合题意。(也就是(sum+sum_j)/count_1<1的时候。)直接输出“ Impossible ”即可。但是,并不是所有的正数都是(sum+sum_j)/count_1。因为,当sum+sum_j不能被count_1整除时就必需要某几个数大于(sum+sum_j)/count_1。(我此处用的是编程的习惯,而不是数学的习惯。编程中把结果存储到一个int型的变量中不是小数。而是小于等于sum+sum_j)/count_1的整数)所以,我就还需要再定义一个变量来存储它的余数(下面用sum_m来代替。)。从使的某几个正数的值是(sum+sum_j)/count_1+1。(正是因为此处要加1所以不使用(sum+sum_j)/count_1与sum作比较)既然已经想好了知道正数的总数和负数总数后我就开始写了代码。
        下面是我根据上述思路所写的代码:(然后我就快乐的WA了,所以大家可以可以无视。)

#include<string>
#include<string.h>
using namespace std;
char c;
int m[1000000];
int main(){
	int count_num=0;
	int count_1=1;
	int count_2=0;
	int count=0;
	memset(m,0,sizeof(m));
	while(cin>>c){
		if(c=='+'){
			count_1++;
			m[count++]=1;
		}
			
		if(c=='-'){
			count_2++;
			m[count++]=2;
		}
			
		if(c=='?')
			count_num++;
		if(c=='='){
			int sum;
			cin>>sum;
			//cout<<count_1<<"/"<<count_2<<"/"<<endl;
			int sum_j=count_2;
			int sum_ad=sum+count_2;			     //这个就是所有正数的和
			if(sum_ad>sum*count_1||sum_ad<count_1){	     //判断是否符合题意,也就是当所有的正数的和大于sum*count_1或者某个								             //正数等于‘0’的时候,sum_ad/count_1<1也就是sum_ad<count_1的时候。
				cout<<"Impossible"<<endl; 	     //直接输出Impossible。
			}
			else{
				cout<<"Possible"<<endl;		     //除了不可能的情况剩下的自然就是可能了。
				int num_k=sum_ad/count_1;	     //使用num_k存入每个正数的基础值。
				int num_m=sum_ad%count_1;	     //使用num_m存入余数,在输出的时候决定输出sum_k+1的个数。
				if(num_m>0){			     //如果余数大于0则有一个正数是sum_k+1,所以第一个数字就输出sum_k+1。
					cout<<num_k+1<<" ";
										num_m--;
					count_1--;
				}
				else{
					cout<<num_k<<" ";	      //如果num_m等于‘0’那么所有的正数都是sum_k。
					count_1--;		      //正数已经输出一个,则正数的数量-1。(多余的一步,仅用来理解。)
				}
				for(int j=0;j<count;j++){	       //接下来有几个符号就输出几个数字。
					if(m[j]==1){
						cout<<"+ ";
						if(num_m>0){		//只要需要输出num_k+1没有输出完全,就继续输出是正数为num_k+1。
							cout<<num_k+1<<" ";
							num_m--; 
							count_1--;
						}
						else{
							if(count_1>0){	//只要正数的数量没有输出完,就继续输出num_k。
								cout<<num_k<<" ";
								count_1--;
							}
							else{		//否则就只输出‘1’其实这个判断没有必要仅用来理解。
								cout<<"1 ";
							}
								
						}
					}
					else{
						cout<<"- 1 "; 		//如果是‘-’号就直接是输出我以为的 1
					}
				}
				cout<<"= "<<sum<<endl;                  //在所有的数字输出完后就直接输出总和sum。
			}
			count_1=1;
			count_2=0;
			count_num=0;
			count=0;
			memset(m,0,sizeof(m));
		}
	}
}



          然而在我提交了错误的代码后发现,我是错在是CF上的第13组数据。结果,这也是我思路上最大的漏洞。请看我的运行结果。


(图1)错误代码的运行结果

        在这组测试中一共有80个正数,19个负数。最终结果等于4。如果按照我的思路确实输出的是Impossible。因为答案是要使所有的负数变为4。而在我的结果中所有负数的值是1所以就错了。


      我本来还想,我是不是只要在是负数的值为最大值就可以解决问题了呢?但是,后来想想这绝对是必错的。当然,我也没有试过。

      于是,我就想能不能让所有负数值(下面用num_j代替。)都从1~sum尝试一次呢?于是,我有重新加入了一个循环:


for(int j=1;j<=sum&&sum_ad<count_1;j++){//因为如果sum_ad的值小于count_1的时候就存在0不符合情况。此时还需要增加负数的值。
	num_j=j;
	sum_j=count_2*j;                    //因为所有的负数(sum_j)都不再是1了所以所有负数的总和就换成了count_2*j。
	sum_ad=sum+sum_j;                   //而所有正数(sum_ad)的总和就是sum+sum_j。
}

         加入这个循环其他之后的思路和我没有差别,最后提交的AC代码是:(更改部分已加粗。)

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
char c;
int m[1000000];
int main(){
	int count_num=0;
	int count_1=1;
	int count_2=0;
	int count=0;
	memset(m,0,sizeof(m));
	while(cin>>c){
		if(c=='+'){
			count_1++;
			m[count++]=1;
		}
		if(c=='-'){
			count_2++;
			m[count++]=2;
		}
		if(c=='?')
			count_num++;
		if(c=='='){
			int sum;
			cin>>sum;
			int num_j,sum_j,sum_ad=0;
			for(int j=1;j<=sum&&sum_ad<count_1;j++){
				num_j=j;
				sum_j=count_2*j;
				sum_ad=sum+sum_j;
			}
			if(sum_ad>sum*count_1||sum_ad<count_1){
				cout<<"Impossible"<<endl;
			}
			else{
				cout<<"Possible"<<endl;
				int num_k=sum_ad/count_1;
				int num_m=sum_ad%count_1;
				if(num_m>0){
					cout<<num_k+1<<" ";
					num_m--;
					count_1--;
				}
				else{
					cout<<num_k<<" ";
					count_1--;
				}
				for(int j=0;j<count;j++){
					if(m[j]==1){
						cout<<"+ ";
						if(num_m>0){
							cout<<num_k+1<<" ";
							num_m--; 
							count_1--;
						}
						else{
							if(count_1>0){
								cout<<num_k<<" ";
								count_1--;
							}
							else{
								cout<<"1 ";
							}
								
						}
					}
					else{
						cout<<"- "<<num_j<<" "; 
					}
				}
				cout<<"= "<<sum<<endl;
			}
			count_1=1;
			count_2=0;
			count_num=0;
			count=0;
			memset(m,0,sizeof(m));
		}
	}
}


下面是我AC后的自我小结:(在此之后的内容都可以无视。)


        这是我做这个题的全过程我其实之后在看了大神的题解后还把这个代码进行了改进。不但使代码的长度有所减短,同时一定程度上还减少了使用的空间和程序运行的时间。不过我改进后的代码就不再此进行展示了。(以上我是在做这个题时完整的过程,如果有大神觉得上面的代码敲的不好或者有点差劲的话希望不要喷我。因为,在这里记录下的才是我做题是最完整的过程。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值