DP, 最优解这里变成了遍历所有可能组合的总数(和一般的有点小不一样)
#include <stdio.h>
unsigned long UnimodalNum[1000][1000] = {0};
void init() {
for(int i = 0; i <1000; i++) {
for (int j = 999; j >= 0; j--) {
if (i == 0) { // N is 0.
UnimodalNum[i][j] = 1; // N is 0, means no change any more, so only 1 combination.
} else if (j == 0) { // start from 0.
UnimodalNum[i][j] = -1;
} else if (i < j) {
UnimodalNum[i][j] = -1;
} else if (i == j) {
UnimodalNum[i][j] = 1;
} else {
UnimodalNum[i][j] = -1;
long uniNum = 0;
long begin = j-1;
while (1) {
// printf("%d\n", begin);
begin++;
if (begin >= (i/2 + 1)) {
break;
} else if (begin*2 == i) {
uniNum += 1;
} else if (i - begin*2 < begin) {
continue;
} else {
if (UnimodalNum[i - 2*begin][begin] == -1) {
uniNum += 1;
} else {
uniNum += UnimodalNum[i - 2*begin][begin];
}
}
}
UnimodalNum[i][j] = uniNum + 1;
}
}
}
}
int main() {
int num;
init();
while(1) {
scanf("%d", &num);
if (num == 0) {
return 0;
}
printf("%d %lu\n", num, UnimodalNum[num][1]);
}
}
题最大的输入 是 231,
所以维护一个232的二维矩阵就够了。
还有一点就是 全部用long(包括中间数据), 不然220以后就溢出了。
着了的 unimodlNum[i][j]的含义是:
总数N为i, 从j开始进行回文不递减排列的组合的个数。
极限情况:
i == 0, 没有意义, 设为 -1.
i == j, 只有一种排列方式.
i < j, 这种情况根本没有排列, 无解, 设 -1;。
j == 0, 也没啥意义, 设为 -1;
一般情况:
从j开始遍历(设遍历变量为begin),在(j每次递增)
当j == 2*i时,组合个数+1 (i, i)
当 j >= i/2+1时,后面已经不再可能有解, 跳出循环.
当 j - 2i < i时, 此组合不是解,但是不能跳出循环, 继续后面(后面还可能有解, 这种情况下不可能在有 (j, ....., j)的组合, 但还可能有(j,j)的组合)
其他的 则是 组合个数 + unimodlNum[i - 2*begin][begin],
再分为: 如果 unimodlNum[i - 2*begin][begin],是一个无效解, 那么组合个数加1(及 (j....j)的组合不可能存在, 但是可以由(j,i-2j,j)的组合)
否则 组合个数+unimodlNum[i - 2*begin][begin]。
这些题深感 主要难处是要去想清楚所有的边边角角.