2068885 2006-09-15 22:28:27 Accepted 1937 C++ 00:00.02 436K St.Crux 给定队列首数1和尾数N,求一个最短升序队列,之中每一个数都可以用队列中其余两个数(可以相等)之和表示。 这题相当的棒。似乎是枚举构造orz,然而可以用搜索dfs,搜到(加到)N时停止并判断长度。但如果用一个二重循环不断搜索,下场就是死。所以还需要优化。 首先需要计算出,对于队列的最后第二个数M,累计到N最小需要多少步。因为2M到M需要一步,所以s[M] = s[M + M] + 1。这是题目的关键。比如49到98是一步,到100就至少要两步。 然后剪枝:如果s[M] + 目前的长度已经不可能再得到最优解时停止。这种剪枝非常常见。我一开始没有考虑SM的情况,结果很惨。 #include <cstdio> int n, ans, a[100], r[100], d[202]; void dfs(int);void print();void init(); int main(){ //freopen("in.txt", "r", stdin); while(scanf("%d", &n) && n) { init(); dfs(0); print(); } return 0;} void init(){ int i; ans = n; a[0] = 1; for(i = n; i <= n + n; i ++) d[i] = 0; for(i = n - 1; i > 0; i --) d[i] = d[i + i] + 1;} void print(){ int i; for(i = 0; i < ans; i ++) { printf("%d ", r[i]); } printf("%d\n", n);} void dfs(int l){ int i, k; if(l + d[a[l]] >= ans) return; //prunation if(a[l] == n) { ans = l; for(i = 0; i < l; i ++) { r[i] = a[i]; } return; } for(i = l; i >= 0; i --) { for(k = i; k >= 0; k --) { a[l + 1] = a[i] + a[k]; if(a[l + 1] > a[l] && a[l + 1] <= n) dfs(l + 1); } }}

评论