数列转换(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)的结构很容易让人发现它的总和守恒;进
一步联想到局部和。
广搜超内存代码:
这题以上来我就是广搜,然后用字典树判重,写的时候就担心会超内存,结果就真的超了…………然后就没辙了……
这题用到守恒定律,看了别人讲的很好理解。
下面部分摘自:这里这里这里
我们分析一下这个题目。
操作是有先后顺序之分的。比如先对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; }
相关文章推荐
- NYOJ109 数列转换(守恒技巧+数学)
- NYOJ 109数列转换
- NYOJ109 数列转换 【守恒法】
- nyoj109 数列转换
- nyoj-109 数列转换 (守恒定律)
- nyoj 109 数列转换
- NYOJ-109 数列转换
- NYOJ-109 数列转换 AC 分类: NYOJ 2014-12-01 00:54 84人阅读 评论(0) 收藏
- nyoj 109 数列转换(equal函数STL)
- NYOJ-109 数列转换 AC
- nyoj 109 数列转换
- NYOJ 109数列转换
- NYOJ109 数列转换 【守恒法】
- NYOJ 109. 数列转换
- nyoj461 Fiboncci数列(4)解通项公式
- 【LeetCode-面试算法经典-Java实现】【109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)】
- 数据结构(C++实现):栈的运用--中缀表达式转换为后缀表达式既 nyoj 257
- NYOJ298点的转换(矩阵十大问题之一)
- NYOJ题目1102Fibonacci数列
- nyoj 21 三个水杯 隐式图转换举例