[SMOJ1780]变形合唱队形

来源:互联网 发布:新网域名备案系统 编辑:程序博客网 时间:2024/05/16 11:53

题目描述

n(编号 0 至 n1)个学生组成的合唱队,已知他们的身高,且没有相同的。现在要你来负责给他们排队,使得他们的身高是“山峰”型的。所谓的“山峰型”是指,存在一个下标 j (0<j<n1), 使得下标从 0 到 j 的人的身高是递增的,从下标是 jn1 的人的身高是递减的。似曾相识?那加个条件吧,我们还要求任意相邻两个人的身高的差距不能超过 k。请问有多少种不同的“山峰”型队列?答案模1234567891。如果没有合法的“山峰”型队列,输出0。

输入格式 1780.in

多组测试数据。
第一行:一个整数 r, 表示有 r 组测试数据。1r5
每组测试数据格式如下:
第一行:nk1n501k1000000000
第二行:n 个正整数,指身高,每个正整数不超过 1000000000。

输出格式 1780.out

r 行,每行一个整数表示有多少种不同的“山峰”型队列。

输入样例 1780.in

2
4 10
1 5 10 4
9 44
96 29 21 90 46 77 31 63 79

输出样例 1780.out

6
126

样例解释

第一组测试数据解释,6个不同合法“山峰”队列分别是:
{1, 4, 10, 5}, {1, 5, 10, 4}, {1, 10, 5, 4}
{4, 5, 10, 1}, {4, 10, 5, 1}, {5, 10, 4, 1}


这题看得我挺头疼的,想在原数组直接搞 DP。但一时要想着身高大小关系,一时又要考虑身高差值。其实后来想想,分析问题的时候,应该把一些条件“强制”一下,比如先把身高排序,这样只考虑差值的问题就可以了。其实这是简化问题的基本策略,但我考试时居然没有意识到,唉。

首先明确一点,既然在最终的队形中是要让 0 ~ j 单调递增,意味着前面的值都比 j 的高度小,同理 j ~ n1 单调递减,意味着后面的值都比 j 的高度小。总而言之,j 是最高点。这是唯一确定的。

我们应该如何描述一个队形呢?既然中心点已经确定,那么剩下的无非就是最左边和最右边。不妨用 f[i][j][k] 表示排序后的前 i 个人组成一种合法队列,其中左、右端点分别为 jk 的方案数。

可以想象这样的情景:因为这个队列从中间往两边是越来越低的,那么我们就可以看作,在确定了最高的中心点之后,其他人从高到低不断往两边插入值。
在这种理解基础上,就不难得到

f[i][i][b]=a=1i1f[i1][a][b] if hahik
f[i][a][i]=b=1i1f[i1][a][b] if hbhik

上述两个式子分别对应:把第 i 个人插入在队列最左端和最右端。

那么初始状态就有 f[1][1][1] = f[2][1][2] = f[2][2][1] = 1,要求的答案为

i=2nf[n][n][i]+f[n][i][n]

解释一下,最后一定是取完了 n 个点,最低的点 n 必定为左端点或右端点(不难证明:假设在最终的队列中 n 不为两边的端点,则由该队列的性质可得,n 的旁边必然还有比它更低的端点,与事实矛盾,故假设不成立),两种情况都有可能,应用的是加法原理。而 i 从 2 开始取是因为整个队列完全单调递增或递减的方案是不合法的(见样例 1),因此 i=1 不能作为最左端或最右端。

这题还有少一维状态的方法,跟之前“多滋味的咖啡”、“中国移动”有异曲同工之妙。但由于最近测试多,题目多,要写的笔记也多,优化暂时还没时间写。

参考代码:

#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <functional>using namespace std;const int mod = 1234567891;const int maxn = 100;int h[maxn];long long dp[maxn][maxn][maxn];int main(void) {    freopen("1780.in", "r", stdin);    freopen("1780.out", "w", stdout);    int r;    scanf("%d", &r);    while (r--) {        int n, k;        scanf("%d%d", &n, &k);        for (int i = 1; i <= n; i++) scanf("%d", &h[i]);        sort(h + 1, h + n + 1, greater<int>());        memset(dp, 0, sizeof dp);        dp[1][1][1] = dp[2][1][2] = dp[2][2][1] = 1;        for (int i = 3; i <= n; i++)            for (int a = 1; a < i; a++)                for (int b = 1; b < i; b++) {                    if (h[a] - h[i] <= k) (dp[i][i][b] += dp[i - 1][a][b]) %= mod;                    if (h[b] - h[i] <= k) (dp[i][a][i] += dp[i - 1][a][b]) %= mod;                }        long long ans = 0;        for (int i = 2; i <= n; i++) (ans += (dp[n][n][i] + dp[n][i][n]) % mod) %= mod;        printf("%lld\n", ans);    }    return 0;}
0 0
原创粉丝点击