ZOJ 2997 Black and White

  题目大意:给三个正整数n, p, q,判断是否存在这样一个长度为n的序列满足:(1)任何一个长度为p的连续子序列的和为正数;(2)任何一个长度为q的连续子序列的和为负数。如果存在这样一个序列,输出一个这样的序列。

  看到这个题目,没有什么想法,不会做...有一个广为流传的zoj题目列表,其中某大牛是这样分析的:

  经典的拓扑排序,构造一个长度为 N 的序列,使得序列所有连续 P 个元素之和为正,且所有连续 Q 个元素之和为负。将问题转化,构造这个序列的累加序列,相当于构造一个长度为 N + 1 的序列 S[0..N],满足 S[i+P] - S[i] > 0 S[i+Q] - S[i] < 0。这样的话,可以构造 N+1 顶点的图,将所有 S[i] > S[j] 的关系创建有向边 (i, j) 。那么,将这个图拓扑排序,然后如果有环,则不可构造,否则,其深搜弹出序号本身即可作为 S[i] 的值。

  看来自己分析问题、转换问题的能力还是好弱啊,还要继续努力啊...

  最开始用了一个G[5000][5000]的数组保存有向图,结果MLE,然后看别人代码,忽然发现没必要去存图,有规律的嘛(最初就没考虑5000*5000的数组会超过内存限制,一直再担心太多递归调用会导致栈溢出),就把G数组去掉了,然后神奇地过了,我还一直担心栈溢出呢,代码如下:

 

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int maxn = 5000+10;
 5 int c[maxn];
 6 int n, p, q;
 7 int cnt;   //the number of    vertexes poped
 8 int ans[maxn];
 9 
10 bool dfs(int u)
11 {
12     c[u] = -1;
13     for(int v = 0; v <= n; v++)
14         if((u - v == p) || (v - u == q))
15         {
16             if(c[v] < 0)   return false;
17             else if(!c[v] && !dfs(v))   return false;
18         }
19     c[u] = 1;
20     ans[u] = ++cnt;
21     return true;
22 }
23 
24 bool toposort()
25 {
26     for(int u = 0; u <= n; u++)
27         if(!c[u])
28             if(!dfs(u))   return false;
29     return true;
30 }
31 
32 int main()
33 {
34 #ifdef LOCAL
35     freopen("in", "r", stdin);
36 #endif
37     while(scanf("%d%d%d", &n, &p, &q) != EOF)
38     {    
39         memset(c, 0, sizeof(c));
40         cnt = 0;
41         if(toposort())
42         {
43             printf("YES\n");
44             for(int i = 1; i <= n; i++)
45             {
46                 printf("%s", i == 1 ? "" : " ");
47                 printf("%d", ans[i] - ans[i-1]);
48             }
49             printf("\n");
50         }
51         else printf("NO\n");
52     }
53     return 0;
54 }

 

 

 

 

posted @ 2013-04-29 16:15  xiaobaibuhei  阅读(229)  评论(0编辑  收藏  举报