FZU Problem 1080 奇怪的数列

Problem 1080 奇怪的数列

Accept: 199 Submit: 343 Time Limit: 1000 mSec Memory Limit : 32768 KB

img Problem Description

有一个长度为n (1<n<=100) 的数列,其中一些元素是正整数,其余元素是0。这些正整数会同时加倍,并将加倍后的数二等分后向左右两侧的元素转移,从而从一个状态转入其后继状态,如下图的一个状态:

0 6 0 8 0

经一次加倍转移后,其后继状态为:

6 0 14 0 8

特别要注意的是:第一个元素的数加倍后,向左边移动的数又回到原处。最后一个元素的数加倍后,向右边移动的数消失。如上述状态再经一次加倍转移后的后继状态是:

6 20 0 22 0

有些状态不可能是另一些状态的后继状态,我们称这样状态为“根状态”。给出一个状态,求它的根状态。

img Input

第一行仅包含一个表示测试例个数的正整数n。以下 2n 行为测试例的输入数据。
每个测试例输入两行,第一行是一个正整数,为数列的长度。第二行为给定的一个数列,两数之间用一个空格隔开。

img Output

每个测试例输出一行,包含数列的根状态下的各个元素,两数之间用一个空格隔开。

img Sample Input

2

5

5 10 1 9 0

8

1 0 1 1 0 3 9 1

img Sample Output

0 0 1 0 0

1 0 1 1 0 3 9 1

img Source

FJNU Preliminary 2005

解决方案

本题主要是找规律来逆推多次,直到求出“根状态”,以题目描述中所给的例子来讲

0 6 0 8 0

6 0 14 0 8

6 20 0 22 0

  1. 假设我们只知道[3](第三排的元素),那么我们就应该先找出从[3]逆推出[2]的方法

  2. 先从[3][5](第三排第五个元素)来看(为什么呢,因为[3][5]仅仅只有可能来自[2][4]的二分),所以[2][4]就等于[3][5]

  3. 得到[2][4]之后,再根据[3][3]=[2][4]+[2][2]来求出[2][2]

  4. 这也就是说我们已知[3](第三排)要求[2],现在已经能求出[2][n-1](第二排倒数第二个元素)、[2][n-3]……以此类推

  5. 接下来考虑,当n为偶数或奇数各会发生什么,如本例n=5,是奇数,按照3.求到[2][2]为0后,根据[3][1]=[2][1]+[2][2],即可求出[2][1]。那么如果n是偶数,按照3.我们最后会直接求得[2][1],然后可以求出[2][2]。

  6. 进行到这里,n为奇数时,我们已经求出了[2][偶数](第二排的偶数项元素)和[2][1];n为偶数时,我们已经求出了[2][奇数]和[2][2]。

  7. 接下来以n为奇数为例继续,根据[3][2]=[2][1]+[2][3]求出[2][3]=20-6=14。以此类推可以把[2]都求出来。

  8. 根据1-6的规律方法就完成了一次逆推。那么如何判断逆推到什么情况不可再逆推了呢?也就是如何判断“根状态”呢?其实只要“根状态”再逆推一次,就会出现负数,只要逆推完发现负数,就可以知道逆推前的就是“根状态”了。

接下来是代码

//转载自:https://blog.youkuaiyun.com/zhuxingfu0625/article/details/7479806
#include<iostream>
using namespace std;
int main()
{
	int t[2][100], i, k, n, Case;
	bool flag;
	cin >> Case;
	while (Case--)
	{
		cin >> n;
		for (i = 0; i < n; ++i)
			cin >> t[0][i];
		k = 0;
		flag = true;
		while (flag)
		{
			t[(k + 1) % 2][n - 2] = t[k][n - 1];
			for (i = n - 4; i >= 0; i -= 2)
				t[(k + 1) % 2][i] = t[k][i + 1] - t[(k + 1) % 2][i + 2];
			if (i == -2)
			{
				i = 1;
				t[(k + 1) % 2][1] = t[k][0] - t[(k + 1) % 2][0];
			}
			else if (i == -1)
			{
				i = 0;
				t[(k + 1) % 2][0] = t[k][0] - t[(k + 1) % 2][1];
			}
			for (i += 2; i <= n - 1; i += 2)
				t[(k + 1) % 2][i] = t[k][i - 1] - t[(k + 1) % 2][i - 2];
			for (i = 0; i <= n - 1; ++i)
			{
				if (t[(k + 1) % 2][i] < 0)
					flag = false;
				else
					k = (k + 1) % 2;
			}
		}
		for (i = 0; i <= n - 2; ++i)
			cout << t[k][i] << " ";
		cout << t[k][n - 1] << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值