数列转换(nyoj 109)

15 篇文章 0 订阅

题目:点击打开链接

这题以上来我就是广搜,然后用字典树判重,写的时候就担心会超内存,结果就真的超了…………然后就没辙了……

这题用到守恒定律,看了别人讲的很好理解。


下面部分摘自:这里这里这里

我们分析一下这个题目。
操作是有先后顺序之分的。比如先对a2操作,再操作a3;先对a3操作,再
操作a2,结果就有天壤之别。
观察Sample:
(1 6 9 4 2 0)à(1 6 13 -4 6 0)à
(1 6 13 2 -6 6)à(7 -6 19 2 -6 6)
数字杂乱无章没有规律。仔细观察一下操作规则:
(ai-1, ai, ai+1)à(ai-1+ai, -ai, ai+ai+1)
直观的看,相当于把中间的数分别加到两边去,然后取反。容易发现,操作
前后的总和是不变的!
我们可能很激动的猜想:只要两个序列和相等,他们就能通过操作互达。
但是第二个sample 很快否定了这个想法:(1 2 3), (1 3 2)是不可达的。
因为(1 2 3)能进行的操作仅仅是:(3 -2 5),再进行一次操作回到(1 2 3)。所
以永远不能变成(1 3 2)。
总和虽然不行,我们可以试着考察局部和,(即前n项和)。
(ai-1, ai, ai+1) (ai-1+ai, -ai, ai+ai+1)
S1=ai-1 S1’=ai-1+ai
S2=ai-1+ai S2’=ai-1
S3=ai-1+ai+ai+1 S3’=ai-1+ai+ai+1
很容易看出S3=S3’,(S1,S2)=(S2’,S1’)。也就是说把(S1,S2,S3)中的S1和S2交换
位置就能得到(S1’,S2’,S3’)。
稍微推广一下:设Si=a1+a2+…+ai,对(ai-1, ai, ai+1)操作本质上和交换Si-1, Si
是等价的。(因为Si-2是固定不变的
比如第一个Sample:
(1 6 9 4 2 0)à(1 6 13 -4 6 0)à
(1 6 13 2 -6 6)à(7 -6 19 2 -6 6)
转化成和之后:
(1 7 16 20 22 22)à(1 7 20 16 22 22)à
(1 7 20 22 16 22)à(7 1 20 22 16 22)

(加粗的是交换的两个数)
比如第二个Sample:
(1 2 3)àS(1 3 6)
(1 3 2)àS(1 4 6)
对(1 2 3)的操作实质上是不断的交换S(1 3 6)中的1, 3。无论如何也不可
能变成(1 4 6),因为4 根本没在S(1 3 6)中出现过!
另外还有一点,参与交换的只有S1~Sn-1,Sn是雷打不动的。所以我们算法出
来了:
1、判断Sn是否相等。
2、判断{S1, S2 ,…, Sn-1}是否相等。
O(nlogn)的时间复杂度(排序复杂度)。
一个看似繁琐的题目被很轻松的解决了。
如上文所言,操作的变化过程是纷繁复杂的,可以说没任何规律。如果从
每一次操作对总体的贡献入手研究,会碰得头破血流。
但是我们把原来的数列进行了一个小小的转化:求和。正是这个求和,使得
操作的本质浮出水面,操作由令人头晕目眩的(ai-1, ai, ai+1)à(ai-1+ai, -ai, ai+ai+1)、
变成了简单的“交换Si-1和Si”。
这是一个应用守恒的经典例子。其中的守恒量是{S1,S2, …,Sn-1},这个集合始
终保持不变,不会有新的元素生成、也不会无缘无故有元素消失;有的只是顺序
上的交换。
抛开琐碎的细节,我们抓住了本质。
为什么会想到求和呢?
因为(ai-1, ai, ai+1)à(ai-1+ai, -ai, ai+ai+1)的结构很容易让人发现它的总和守恒;进
一步联想到局部和。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int a[1100], b[1100];

int cmp(const void *a, const void *b)
{
	return (*(int *)a - *(int *)b);
}

int main (void)
{
	int n;
	scanf("%d", &n);
	while(n --)
	{
		int m, i;
		scanf("%d", &m);
		for(i = 0; i < m; i++)
			scanf("%d", &a[i]);
		for(i = 0; i < m; i++)
			scanf("%d", &b[i]);
		
		for(i = 1; i < m; i++)
		{
			a[i] += a[i - 1];
			b[i] += b[i - 1]; 
		}
		qsort(a, m , sizeof(a[0]), cmp);
		qsort(b, m, sizeof(b[0]), cmp);
		int f = 0;
		for(i = 0; i < m; i++)
		{
			if(a[i] != b[i])
			{
				f = 1;
				break;
			}
		}
		if(f == 1)
			printf("No\n");
		else
			printf("Yes\n");
		
	}
	return 0;
}

广搜超内存代码:

#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;

struct node
{
	int s[1010];
};
struct tree
{
	int i;
	tree* next[2010];
	tree()
	{
		for(i = 0; i < 2010; i++)
			next[i] = NULL;
	}	
};
queue <node> q;
node start, end;
int n;


int vis(tree* head, node e)
{
	tree * temp = head;
	int i;
	int t, ok = 1;
	for(i = 0; i < n; i++)
	{
		t = e.s[i] + 1000;
		if(temp ->next[t] == NULL)
		{
			ok = 0;
			tree *x = new tree;
			temp->next[t] = x;
		}
		temp = temp ->next[t];
	}
	return ok;
}

int cmp(int a[], int b[])
{
	int i;
	for(i = 0; i < n; i++)
	{
		if(a[i] != b[i])
			return 0;
	}	
	return 1;
}

int bfs()
{
	tree * head = new tree;
	int i;
	q.push(start);
	int ok = vis(head, start);
	while(!q.empty())
	{
		node e = q.front();
		q.pop();
		for(i = 1; i < n - 1; i++)
		{
			node e1 = e;
			e1.s[i - 1] = (e1.s[i]) + (e1.s[i - 1]);
			e1.s[i + 1] = (e1.s[i]) + (e1.s[i + 1]);
			e1.s[i] = -(e1.s[i]);
			if(vis(head, e1) == 0)
			{
				if(cmp(e1.s, end.s) == 1)
					return 1;
				q.push(e1);
			} 
		}	
	}
	return 0;
}

int main (void)
{
	int t;
	scanf("%d", &t);
	while(t --)
	{
		int i;
		while(!q.empty())
			q.pop();
		scanf("%d", &n);
		for(i = 0; i < n; i++)
			scanf("%d", &start.s[i]);
		for(i = 0; i < n; i ++)
			scanf("%d", &end.s[i]);
		if(bfs() == 1)
			printf("Yes\n");
		else
			printf("No\n");
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值