标签:
题目链接:7304
题目大意:士兵过山洞,必须以类似7 6 5 4 3 2 1顺序过。在第i个人之后,比i高的人都会被杀死,问如果要杀死k个人,有几种排队方法。
题目思路:先将士兵的身高离散化。假设N表示不同身高的数目。cnt[i] 表示i这个身高的人有多少个。(i的范围为1~N)sum[i]表示小于等于该身高段的士兵数目
然后开始dp,dp[i][j]表示已经到第i个士兵,已经死了j个人的方法数。
第三维遍历,q表示,第i+1这个身高段死了q个人。
dp[i + 1][j + q] : 到第i + 1这个身高段,死了j + q个人
dp[i + 1][j + q] = dp[i][j] * C(sum[i] + q - 1,q) (C表示求组合数)
eg: 222111,死一个人,将q个3排进去的方法数为,插空总共有sum[i] + q个位置,取q个。即C(sum[i] + q,q),因为最前面这个位置不能排,3死不掉,所以是C(sum[i] + q - 1,q);
注意求组合函数,先求阶乘再求逆元。
注意dp初始化。
以下是代码:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#include <deque>
#include <list>
using namespace std;
typedef long long ll;
int N; //不同高度的士兵个数
int num[50005];
int cnt[105]; //i高度的士兵个数
int sum[105]; //小于等于i高度的士兵个数
int n,k;
long long dp[105][2005];
#define mod 1000000007
//====求组合函数===
#define MAXV 52000
long long fact[MAXV];
long long inv[MAXV];
inline long long expo(long long a, long long b)
{
long long res = 1;
while(b != 0)
{
if (b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>=1;
}
return res;
}
inline long long binomial(int n,int k)
{
long long res = ((long long)inv[k] * inv[n - k]) % mod;
return (res * fact[n]) % mod;
}
void Generate()
{
int i;
fact[0] = 1;
for (i = 1; i < MAXV; i++) fact[i] = ((long long)fact[i - 1] * i) % mod;
for (i = 0; i < MAXV; i++) inv[i] = expo(fact[i], mod - 2);
}
//=====END======
void solve()
{
memset(dp,0,sizeof(dp));
for (int i = 0; i < 103; i++) dp[i][0] = 1;
for (int i = 1; i < N; i++) //到第i个人
{
for (int j = 0; j <= k; j++) //死了j个人
{
for (int q = 0; q <= cnt[i+1] && j + q <= k; q++) //i+1这个高度,死了q个人(比如3这个高度有4个人,q可以是0,1,2,3,4)
{
if (j + q == 0) continue;
dp[i + 1][j + q] = (dp[i + 1][j + q] + dp[i][j] * binomial(sum[i] + q - 1,q)) % mod;
}
}
}
}
int main()
{
int t;
cin >> t;
int Case = 1;
Generate();
while(t--)
{
scanf("%d%d",&n,&k);
N = 1;
for (int i = 0; i < n; i++)
{
scanf("%d",&num[i]);
}
sort(num,num + n);
memset(cnt,0,sizeof(cnt));
int p = 1;
for (int i = 1; i < n; i++)
{
if (num[i] != num[i - 1])
{
cnt[N++] = p;
p = 1;
}
else p++;
}
cnt[N++] = p;
sum[0] = 0;
for (int i = 1; i < N; i++)
{
sum[i] = sum[i - 1] + cnt[i];
}
solve();
printf("Case %d: ",Case++);
printf("%lld\n",dp[N-1][k] % mod);
}
return 0;
}
/*
1
10 5
1 2 3 4 4 4 4 4 4 4
//86
1
10 6
1 2 3 4 4 4 5 5 6 10
//61180
1
10 4
1 2 3 4 4 4 5 5 6 10
//10385
1
10 5
1 2 3 4 4 4 5 5 6 10
//30101
UVALive-7304 - Queue of Soldiers 【动态规划】【组合函数】【好题】
标签:
原文地址:http://blog.csdn.net/loy_184548/article/details/52166744