题目大意:给三个正整数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数组去掉了,然后神奇地过了,我还一直担心栈溢出呢,代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }