排队(递推)

【问题描述】

      小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的值。

【输入样例1】

5 3

【输出样例1】

15

【数据范围】

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

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

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

此题是求逆序对。一开始想到暴力算法——直接枚举,结果只过了一组数据。。。

后来经同学讲解,原来正解是用递推算法,很后悔当时写完枚举后没有用小数据找规律。。。。。。

用递推,f[i,j]表示{an}前i项之和为j的方案数,f[i,j]=f[i-1,j]+f[i-1,j-1]+…+f[i-1,j-i+1]。时间复杂度O(n2k)。

优化:将j变成j-1,可以得出f[i,j-1]=f[i-1,j-1]+…+f[i-1,j-i] 两式相减得:

f[i,j]=f[i-1,j]+f[i,j-1]-f[i-1,j-1]

这也给了我一个教训,写完暴力算法后要学会用小数据找正解的思路。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
long long f[305][5000];
long long n,k;
long long sum=0;
int main()
{
  freopen("lineup.in","r",stdin);
  freopen("lineup.out","w",stdout);
  cin>>n>>k;
  for(int i=1;i<=n;i++)
  f[i][0]=1;
  for(int i=1;i<=n;i++)
  {
    sum+=i-1;
    for(int j=1;j<=k;j++)
    {
      if(j>sum)break;
      f[i][j]=(f[i][j-1]+f[i-1][j])%1799999;
      if(i<=j)
      f[i][j]=(f[i][j]-f[i-1][j-i]+1799999)%1799999;
    }
  }
  cout<<f[n][k]%1799999;
  return 0;
}

原文地址:https://www.cnblogs.com/937337156Zhang/p/5674890.html