神奇的Noip模拟试题一试 2 排队

2 排队

(lineup.pas/.c/.cpp)

【问题描述】

小sin所在的班有n名同学,正准备排成一列纵队,但他们不想按身高从矮到高排,那样太单调,太没个性。他们希望恰好有k对同学是高的在前,矮的在后,其余都是矮的在前,高的在后。如当n=5,k=3时,假设5人从矮到高分别标为1、2、3、4、5,则(1,5,2,3,4)、(2,3,1,5,4)、(3,1,4,2,5)都是可行的排法。小sin想知道总共有多少种可行排法。

【输入】

输入文件名为lineup.in。

一行两个整数n和k,意义见问题描述。

【输出】

输出文件名为lineup.out。

输出一个整数,表示可行排法数。由于结果可能很大,请输出排法数mod 1799999的值。

【输入输出样例】

lineup.in

lineup.out

5 3

15

【数据范围】

对于20%的数据,有n≤10,k≤40;

对于60%的数据,有n≤100,k≤500;

对于100%的数据,有n≤100,k≤n*(n-1)/2。

解题报告

  像这样的题,也正如这套题一样的完全是思考的时间多。(哎,说得好像我做出来了一样,但其实我连样例都没有去想“天,15个,算了不想了。”所以啊,还是要有耐心,考场上一定要静下来,不要急躁,有同学用了两个小时找规律,最终不负有心人,全过了。)

  首先,我们需要找规律,而找规律不是随机就找到了,需要观察和对比两组甚至多组数据。于是,打开打表模式:

n\k  0   1   2   3   4   5   6

1     1   0   0   0   0   0   0

2     1   1   0   0   0   0   0

3     1   2   2   1   0   0   0

4     1   3   5   6   5   3   1

5     1   4   9  15  20 22 20 .......

   每一个n都有一个k的数量的限制,即长度 l ,如果k>l 就为0 ,l =0,1,3,6,等等。l+=i-1;  每一次都是在上一次的基础上添一个比上一次最大值大1的数,如下:

上一次:1 2 3 4 5

这一次添一个6 有6 种添法:在5后,在4~5间,在3~4间,在2~3间,在1~2间,在1前;

分别有6种增加的可能性:    不影响,多一组,  多两组,   多三组,   多四组,    多五组

  因此可以列出方程:f[i][j]( i 个数,取 j 组)=f[i-1][j]+f[i-1][j-1]+...+f[i-1][j-i+1]

    (转换):f[i][j-1]=f[i-1][j-1]+f[i-1][j-2]+...+f[i-1][j-i]

    (二式相减):f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-i]

  方程有了,代码就简单多了。

 #include<iostream>
#include<cstdio>
using namespace std;
int n,k;
int f[][];
const int inf=;
int main()
{
freopen("lineup.in","r",stdin);
freopen("lineup.out","w",stdout);
cin>>n>>k;
int l=;
for (int i=;i<=n;i++)
{
l+=i-;
f[i][]=;
for (int j=;j<=k;j++)
{
if (j>l) break;
f[i][j]=(f[i-][j]+f[i][j-])%inf;
if (j-i>=) f[i][j]=(f[i][j]-f[i-][j-i]+inf)%inf;
}
}
cout<<f[n][k];
return ;
}
上一篇:unittest测试套件


下一篇:nopCommerce的源代码结构和架构