您的位置:首页 > 其它

数列转换(nyoj 109)

2014-04-13 19:22 232 查看
题目:点击打开链接

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

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

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

我们分析一下这个题目。

操作是有先后顺序之分的。比如先对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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: