1023 Have Fun with Numbers

Notice that the number 123456789 is a 9-digit number consisting exactly the numbers from 1 to 9, with no duplication. Double it we will obtain 246913578, which happens to be another 9-digit number consisting exactly the numbers from 1 to 9, only in a different permutation. Check to see the result if we double it again!

Now you are suppose to check if there are more numbers with this property. That is, double a given number with k digits, you are to tell if the resulting number consists of only a permutation of the digits in the original number.

Input Specification:

Each input contains one test case. Each case contains one positive integer with no more than 20 digits.

Output Specification:

For each test case, first print in a line "Yes" if doubling the input number gives a number that consists of only a permutation of the digits in the original number, or "No" if not. Then in the next line, print the doubled number.

Sample Input:

1234567899

Sample Output:

Yes
2469135798

请注意,数字123456789是一个 9 位数字,正好由 1 到 9 之间的数字组成,没有重复。加倍它将得到246913578,它恰好是另一个 9 位数字,正好由 1 到 9 的数字组成,只是排列不同。检查结果,如果我们再次加倍!   现在,您应该检查此属性是否有更多数字。也就是说,将给定的数字加倍,将k位数字加倍,您将判断结果数字是否仅由原始数字中的数字排列组成。   

输入规范:每个输入包含一个测试用例。每个案例包含一个不超过 20 位的正整数。  

 输出规范:对于每个测试用例,如果将输入数字加倍得到的数字仅由原始数字中的数字排列组成,则首先打印一行“是”,如果不是,则打印“否”。然后在下一行中,打印双倍的数字。


思考:

爆int怎么办?可以直接用字符串计算吗?

不行,字符串不能直接计算

Long long 够长,20位可以覆盖

可以使用排序来对应一一数字是否相同,如果不同就说明不是.

虽然不能直接计算很大的数字,但是数字的每一位我们还是能够计算的。

可以从低位慢慢计算到高位.

题目有说是由原始数字排列而成,因此计数是最好的办法。

数据大了的核心就在于手写计算方法


解题过程

先是采用了直接计算,果不其然大的数据会爆int

#include
#include
#include
#include
#include
#include
using namespace std;

//小数据适用解法.
int main()
{
	long long n, m;
	cin >> n;
	m =(long long) 2 * n;
	string a = to_string(n);
	string b = to_string(m);
	sort(a.begin(), a.end());
	sort(b.begin(), b.end());
	for (int i = 0; i < a.size(); i++)
	{
		if (a[i] != b[i])
		{
			cout << "No" << endl;
			cout << m;

			return 0;
		}

	}

	cout << "Yes" << endl;
	cout << m;
   


	return 0;
}

//果然没这么简单,对于长数字的处理是不能够直接运算的,long long一样爆
//还是只能用字符串,不能投机取巧了
//一旦数字大了,就不能使用编译器的加减乘除了,必须自己用字符串手写计算方法

 AC

#include
#include
#include
#include
#include
#include
using namespace std;

int main()
{
	string N;
	int a[10] = { 0 };
	int b[10] = { 0 };
	int doub[30] = { 0 };
	cin >> N;
	bool flag = true;
	int j = 0;//表示加倍后的字符长度
	for (int i = N.size()-1; i>=0; i--)//必须要倒序,因为必须是从个位开始进位!
	{
		int t = N[i] - '0';
		a[t]++;
		int n = doub[j] + t * 2;
		if (n < 10)
			doub[j] = n;//每次都是细节问题,j又写成i!!!
		else
		{
			doub[j] = n % 10;
			doub[j+1] = n / 10;
			//为了防止溢出和方便输出,只能单独开一个变量
		}
		
		//其实直接看if,else就可以知道为什么最后一个数有不同了
		//最后一个数n<10时,j下标是没有下一位的,刚好足够所以
		//最后一次j++就刚好为加倍后数字的长度
			//而如果n>=10,就多了一个j+1,所以j还要多加一次。

		j++;
		if (i == 0 && n >= 10)
		{
			j +=1;//如果最后进位了就加1
		}
		//就从第一次循坏开始分析,
		//j一开始当作下标使用,每一轮循环都会j++
		//第一次循环后j就是1,也就是当前确定的字符长度,
		//个位是已经被确定了
		//所以一直到最后,因为i循环的长度是N的长度,
		//每一次循环后j都代表着当前的长度
		//那最后j就能完全输出完N的长度,也就是至少已经到达了N的长度
		//而如果第一位进位,那就是N长度之外的,循环已经结束,
		//因为j已经到达了N的长度。所以要自己额外加1。

		//但是这么做很复杂,为了提高可读性。我们索性就直接把j当作下标的移动来使用
		//我们自己再开一个变量表示加倍后数字的长度。无非就两种情况,
		//要么与N同长,要么就是多出一位自己再加一就行了
		//这样,我就不用考虑j是否是加倍数字的长度,随便它怎么变都行

		//还是不设变量了。因为本质上不是为了求长度,而是标记目前数据的位置
		//为了后序输出,j同时是作为长度又是确定下标位置,不能够单单理解成数字长度
		//因此j的设计还是最优的表达。
		
		//最后的j表示加倍后的字符长度,j-1就表示最后一个数字的下标(因为从0开始记录)

	}
	//j能不能换个更简洁的表达?
	//不好换,因为后面很多情况都要牵扯到j
	//doub数组也要使用,下标不对齐比较的时候就会很乱很复杂
	//之所以不使用i就是因为i并不能代表着加倍后数字的长度,一旦
	//加倍后多了一位那就会越界
	//而如果往前移动一位,那下标0就会空出,又与N数组无法对齐了

	for (int i = 0; i < j; i++)
		b[doub[i]]++;

	//for (int i = 0; i < j; i++)
	for (int i = 0; i < 10; i++)//不是j的长度而是只有0-9的数字比对!
	{
		if (a[i] != b[i])
		{
			flag = false;
			break;
		}
	}
	if (flag)
		cout << "Yes" << endl;
	else
		cout << "No" << endl;
	for (int i =j-1; i>=0; i--)
	{
		cout << doub[i];
	}
		



	return 0;
}

 补充解析

#include 
#include 
using namespace std;
int main()
{
    string N;
    cin >> N;
    int doub[21] = { 0 };
    int len = N.length();
    int j = 0;
    int a[10] = { 0 }, b[10] = { 0 };
    int flag = 0;
    for (int i = len - 1; i >= 0; i--)//计算doub
    {
        int t = N[i] - '0';//-'0'将字符型转化为整型
        a[t]++;//N每位数的个数
        int n = doub[j] + t * 2;
        //实现单个数字的加倍
        //每次都是当前数+新数
        if (n < 10) 
            doub[j] = n;
        else
        {
            doub[j] = n % 10;//进位
            doub[j + 1] = n / 10;
            //因为每次都是最新的一个数位
            //所以直接赋值为整除结果即可,然后下一轮再与下一位数字相加
           
        }
        j++;
        //前面不管什么情况,自然j都要前往下一位,但最后一位就不同了。
        //如果最后一位不需要进位,那么每一次的自增j就会自动加1,也就恰好等于数字的个数了
        //而如果下一位进位了,数位增加了,就需要自己再自增j
        if (i == 0 && n >= 10) 
            j++;//第一位需进1的情况
        //j实际上指的是数字的个数,因为我们使用下标时,是从0开始的,所以最后要多加一次j
        
      
    }
    for (int i = 0; i < j; i++)
    {
        b[doub[i]]++;//doub每位数的个数
    }

    //原理跟哈希相同,只不过巧妙利用了单个数字只可能在1-10之间
    //如果范围大了就会浪费空间,用哈希才是最通用的.
    for (int i = 0; i < 10; i++)
    {
        if (a[i] == b[i]) continue;
        else
        {
            flag = 1; break;
        }
    }
    if (flag == 0) cout << "Yes";
    else cout << "No";
    cout << endl;
    for (int i = j - 1; i >= 0; i--)
        cout << doub[i];
    return 0;
}

 后面是尝试不使用计数的方法,而是排序的方法,但是失败了,就作为笔记参考吧

//排序算法困难
#include
#include
#include
#include
#include
#include
using namespace std;

int main()
{
	//如果使用排序的方法,那么就不能直接使用string,因为数组空间不确定
	// 要统一空间,所以要设置长度大小。
	//如果要做到最大化优化就需要设置很多条件,
	// 因为不确定加倍后的字符长度。
	//而且比对字符串时需要到哪里才算停止。
	// 还有就是初始化数组有0排序会遇到的问题
	//所以索性判断的时候就遍历完数组。
	//循环几十次时间也小,没有必要少一点点次数而写一堆代码

	//因为插入字符串只会设置足够大小的空间,所以要自己再创建一个设置好空间大小的string 来实现

	//排序完后,输出又是个问题,从哪里开始输出,然后又要开一个数组储存原始数据,太麻烦还是记录次数的好
	//放弃了。

	//之所以会出现这么多问题就是因为我们是将数字当作字符串输出的,
	// 既然如此就必须要确定从哪里开始输出
	//不能输出多也不能少,而且顺序不能够改变!
	//所以最简单还是记录出现次数,排序太繁琐了。

	//数组必须是排满比对才好用排序,如果是有多余空间的,
	// 就一定不能改变顺序,而且必须要自己建立下标记录
	//数据的位置确保数据的正确输出!

	string N;

	int doub[25] = { 0 };
	//  cin >> N;
	   //N.resize(25);//必须先插入后设置大小,不然插入会直接覆盖你原先改的大小。err
	  //这么写不行,后面的空间全部都是/0而不是0,所以不能直接这么写。

	cin >> N;
	string newN;
	newN.resize(25);
	for (int i = 0; i < N.size(); i++)
		newN[i] = N[i];
	bool flag = true;
	int j = 0;//表示加倍后的字符长度
	for (int i = N.size() - 1; i >= 0; i--)//必须要倒序,因为必须是从个位开始进位!
	{
		int t = newN[i] - '0';

		int n = doub[j] + t * 2;
		if (n < 10)
			doub[j] = n;//每次都是细节问题,j又写成i!!!
		else
		{
			doub[j] = n % 10;
			doub[j + 1] = n / 10;
			//为了防止溢出和方便输出,只能单独开一个变量
		}

		j++;
		if (i == 0 && n >= 10)
		{
			j += 1;//如果最后进位了就加1
		}


	}


	sort(newN.begin(), newN.end());//字符用begin
	sort(doub, doub + 25);//整型用直接+


	//for (int i = 0; i < j; i++)
	for (int i = 0; i < 25; i++)//不是j的长度而是只有0-9的数字比对!
	{
		if (N[i] != doub[i] - '0')//记得转换整型为字符
		{
			flag = false;
			break;
		}
	}
	if (flag)
		cout << "Yes" << endl;
	else
		cout << "No" << endl;
	for (int i = j - 1; i >= 0; i--)
	{
		cout << doub[i];
	}




	return 0;
}

你可能感兴趣的:(算法)