第九题 序列计数
题目
【问题描述】
小明想知道,满足以下条件的正整数序列的数量:
1. 第一项为 n;
2. 第二项不超过 n;
3. 从第三项开始,每一项小于前两项的差的绝对值。
请计算,对于给定的 n,有多少种满足条件的序列。
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
【样例输入】
4
【样例输出】
7
【样例说明】
以下是满足条件的序列:
4 1
4 1 1
4 1 2
4 2
4 2 1
4 3
4 4
【评测用例规模与约定】
对于 20% 的评测用例,1 <= n <= 5;
对于 50% 的评测用例,1 <= n <= 10;
对于 80% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000。
思路分析:
可以按照题目要求设计dfs搜索函数,记录总结点个数减一就是解.但是这样的复杂度太高了,最坏情况下2^1000.
long long t=0; int a[1005]; void dfs(int cur){ t++; cout<
可以改变递归函数的设计方式,采用记忆化搜索.设f(i,j)为上一数是i当前数是j的总序列个数,则从第二个数开始f(i,j)=f(i,1)+f(i,2)+...f(i,abs(i-j)-1).最终解f(n)=f(n,1)+f(n,2)+f(n,3)+....f(n,n).解空间是N的平方(详细为N*N)表格,但是因为每次都要循环加总,所以成了N的立方.
#include
#include #include #include #define _for(i, x, y) for(register int i = x;i <= y;i++) #define _fordw(i, x, y) for(register int i = x;i >= y;i--) typedef long long LL; using namespace std; int N; LL ans; const int MOD = 10000; int mem[1001][1000]; LL dfs(int pre, int cur) { // 询问状态 if (mem[pre][cur] != 0) return mem[pre][cur]; LL ans = 1; _for(j, 1,abs(pre-cur) - 1) { ans = (ans + dfs(cur, j)) % MOD; } //记录状态 mem[pre][cur] = ans; return ans; } void work() { ans = 0; cin >> N; // f(pre,cur) = sum(f(cur,_new))|_new from 1 to abs(pre-cur)-1 _for(x, 1, N) ans = (ans + dfs(N, x)) % MOD; cout << ans << endl; } 在同样的解空间下,避免循环加总,即可优化到N的平方,重新考虑状态的转移:如果我们用f(i,j)表示前一个数是i,当前数[1,j]的合法序列的个数;有f(i,j) = 1 + f(i,j-1) + f(j,abs(i-j)-1)即分为两个部分1)i作为前一个数,从1到j-1为当前数的合法序列的个数已经计算好,2)求以j为尾数,后面选择1到abs(i-j)-1的合法序列的个数。如 f(10,5)=f(10,4)+f(5,4)+1;而不是枚举1到5;这样每次解答树只展开两个节点,相当于减少一层循环,虽然解答树的层次还是很深,但是由于记忆的存在,解空间仍然是N的平方。可在100ms内解决。
#include
#include #include #include typedef long long LL; using namespace std; int N; const int MOD = 10000; int mem[1001][1000]; int dfs(int pre, int cur) { if (cur <= 0) return 0; if (mem[pre][cur] != 0) return mem[pre][cur]; return mem[pre][cur] = (1 + dfs(pre, cur - 1) + dfs(cur, abs(pre - cur) - 1)) % MOD; } void work() { cin >> N; cout << dfs(N, N) << endl; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int a = clock(); work(); int b = clock(); clog << (b - a) << endl; return 0; }
第十题 晚会节目单
【问题描述】
小明要组织一台晚会,总共准备了 n 个节目。然后晚会的时间有限,他只能最终选择其中的 m 个节目。
这 n 个节目是按照小明设想的顺序给定的,顺序不能改变。
小明发现,观众对于晚会的喜欢程度与前几个节目的好看程度有非常大的关系,他希望选出的第一个节目尽可能好看,在此前提下希望第二个节目尽可能好看,依次类推。
小明给每个节目定义了一个好看值,请你帮助小明选择出 m 个节目,满足他的要求。
【输入格式】
输入的第一行包含两个整数 n, m ,表示节目的数量和要选择的数量。
第二行包含 n 个整数,依次为每个节目的好看值。
【输出格式】
输出一行包含 m 个整数,为选出的节目的好看值。
【样例输入】
5 3
3 1 2 5 4
【样例输出】
3 5 4
【样例说明】
选择了第1, 4, 5个节目。
【评测用例规模与约定】
对于 30% 的评测用例,1 <= n <= 20;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 100000,0 <= 节目的好看值 <= 100000。
错误思路
如果用两次排序求解,那就错了。因为并不是要选出的方案的好看值总和最大,而是要从前往后尽量好看。思路 O(N^2)
此题关键在于“第一个节目尽可能好看”并希望“第二个节目尽可能好看”……那么我们选择的第一节目就是max(g[0]~g[n-m])闭区间,要选择的第二个节目是max(g[lastMax+1],g[n-m+1])及从上一个节目往下到n-m+1这个区间里面选最好看的,直到剩下的必须全部选择。算法用尺取法,双指针移动。理论上的复杂度是O(M*(N-M)),极端情况是M=N/2,整体达到(N^2)/4。如果输入数据为
int N, M; void work() { cin >> N >> M; vector
games(N); for (int i = 0; i < N; i++) { cin >> games[i]; } int pos_max = 0, pos_1 = 0, pos_2 = N - M; while (pos_1 < pos_2 && pos_2 < N) { while (pos_1 < pos_2) if (games[++pos_1] > games[pos_max])pos_max = pos_1; cout << games[pos_max] << " "; pos_1 = pos_max + 1; pos_2++; pos_max = pos_1; } while (pos_2 != N) { cout << games[pos_2++] << " "; } cout << endl; } 优化:区间最值查询 O(NlogN)
while (pos_1 < pos_2) if (games[++pos_1] > games[pos_max])pos_max = pos_1;
这一段代码是区间内查询最大值,反复多次,且数据是静态的,所以选择ST做RMQ。f[i][j]表示以 i 为起点,连续 2^j 个数中的最大值(的下标);转移方程就是:f[i][j] = max(data[f[i][j-1]] ,data[f[i+pow_2(j-1)][j-1]])注:比较原始数据,记录下标由于预处理是O(nlogn),M次查询是O(M),每次查询是O(1),所以整体复杂度为O(nlogn)。下列代码实测运行时间100ms以内
#include
#include using namespace std; int N, M; vector data; /*===st rmq的数据 begin===*/ const int MAX_N = 100005; const int MAX_POW = 20; int f[MAX_N][MAX_POW], Log[MAX_N]; /*===st rmq的数据 end===*/ /*===st rmq的函数 begin===*/ void initLog() { Log[1] = 0;//2的0次方为1,log2_1 = 0; for (int i = 2; i <= N; ++i) { Log[i] = Log[i / 2] +1; } } //2的p次方 int pow_2(int p) { return 1 << p; } void initSt() { int i, j; for (i = 0; i < N; ++i) { f[i][0] = i;//注意这里存的是下标,而不是值 } for (j = 1; pow_2(j) < N; ++j) { for (i = 0; i + pow_2(j-1) < N; ++i) { int index1 = f[i][j - 1]; int index2 = f[i + pow_2(j - 1)][j - 1]; f[i][j] = data[index1] > data[index2] ? index1 : index2; } } } int query(int l,int r){ int len = r-l+1; int k = Log[len]; int index1 = f[l][k]; int index2 = f[r - pow_2(k) + 1][k]; return data[index1] > data[index2] ? index1 : index2; } /*===st rmq的函数 end===*/ void work() { std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> N >> M; data = vector (N); for (int i = 0; i < N; i++) { cin >> data[i]; } /*===初始化st rmq相关数据===*/ initLog(); initSt(); int pos_max = 0, pos_1 = 0, pos_2 = N - M; while (pos_1 < pos_2 && pos_2 < N) { /* while (pos_1 < pos_2) if (data[++pos_1] > data[pos_max])pos_max = pos_1;*/ pos_max = query(pos_1,pos_2); cout << data[pos_max] << " "; pos_1 = pos_max + 1; pos_2++; //pos_max = pos_1; } while (pos_2 != N) { cout << data[pos_2++] << " "; } cout << endl; } int main() { /* 造数据 freopen("E:\\data\\my10_1.in", "w", stdout); cout<<100000<<" "<<50000<