标签:second bit 个人 决策 min can its 总结 end
题意:
有一场比赛,有两支队伍,每支队伍有n(<=500)个人,每个人的能力值不同,一支队伍的一个人和另外一支队伍的人PK,每个人只能战斗一次,能力值大的赢并且加一分,给出一个数K,问两支队伍分数相差为K的方案数。
题解:
方法一:
首先这是一道排列计数的问题,首先将所有人的能力值全部从小到大排一次序,这样是为了决策的单调性,只能从前面转移到后面。
很容易定义状态dp[i][a][b]表示前i个人的对决中,第一支队伍得了a分,第二支队伍得了b分的方案数。
现在考虑转移一个人要么对该支队伍贡献一分,要么就让后面的人击败他。
① dp[i][a][b] += dp[i-1][a][b] 表示让后面的人击败他
② dp[i][a][b] += dp[i-1][a-1][b] * (cnt2 - (a - 1) - b) 表示目前的i个人是第一支队伍的击败他之前没有战斗过的第二支队伍的人
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 6e2 + 7;
const int mod = 1e9 + 7;
int n, S[N], B[N], K, cnt1, cnt2;
typedef pair <int , int> pii;
pii xi[N];
LL dp[N][N/2][N/2], ans;
int main ()
{
scanf ("%d%d", &n, &K);
for (int i = 1; i <= 2 * n; ++ i)
{
scanf ("%d", &xi[i].first);
if (i <= n) xi[i].second = 1;
else xi[i].second = 2;
}
sort (xi + 1, xi + 1 + 2 * n);
dp[0][0][0] = 1;
for (int i = 1; i <= 2 * n; ++ i)
{
cnt1 += xi[i].second == 1;
cnt2 += xi[i].second == 2;
for (int a = 0; a <= min (cnt1, cnt2); ++ a)
for (int b = 0; a + b <= min (cnt1, cnt2); ++ b)
{
if (xi[i].second == 1)
{
dp[i][a][b] = (dp[i][a][b] + dp[i - 1][a][b]) % mod;
if (a) dp[i][a][b] = (dp[i][a][b] + (LL)dp[i - 1][a - 1][b] * (cnt2 - a - b + 1) % mod) % mod;
}
if (xi[i].second == 2)
{
dp[i][a][b] = (dp[i][a][b] + dp[i - 1][a][b]) % mod;
if (b) dp[i][a][b] = (dp[i][a][b] + (LL)dp[i - 1][a][b - 1] * (cnt1 - a - b + 1) % mod) % mod;
}
}
}
if ((n ^ K) & 1) cout << "0" << endl;
else if (K == 0) cout << dp[2 * n][n / 2][n / 2] << endl;
else cout << (dp[2 * n][(n + K) / 2][(n - K) / 2] + dp[2 * n][(n - K) / 2][(n + K) / 2]) % mod << endl;
return 0;
}
方法二:
这是一个比较精妙的dp吧~
定义状态dp[i][j]表示第一支队伍前i个人中得j分的方案数。
那么考虑转移
dp[i][j] = dp[i-1][j] (继承前一个状态的方案)
dp[i][j] = dp[i-1][j-1] * (num[i] - j + 1) (针对于第i可以赢的方案)
令h[i] = dp[n][i] * (n - i)! 表示前n个人至少的i分的方案,g[i]表示n个人刚好得i分的方案
g[i] = h[i] - (g[i+1] * C(i+1, i), + g[i+2] * C(i+2,i) + g[n] * C(n,i))
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e3 + 7;
const int mod = 1e9 + 7;
int ai[N], bi[N], n, K, num[N];
LL fac[N], C[N][N], f[N][N], g[N];
int main ()
{
scanf ("%d%d", &n, &K);
for (int i = 1; i <= n; ++ i) scanf ("%d", &ai[i]);
for (int i = 1; i <= n; ++ i) scanf ("%d", &bi[i]);
sort (ai + 1, ai + 1 + n);
sort (bi + 1, bi + 1 + n);
fac[0] = 1;
for (int i = 1; i < N; ++ i) fac[i] = fac[i - 1] * i % mod;
for (int i = 0; i < N; ++ i) C[i][0] = 1;
for (int i = 1; i < N; ++ i)
for (int j = 1; j < N; ++j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= n; ++ j)
if (ai[i] > bi[j]) ++ num[i];
f[0][0] = 1;
for (int i = 1; i <= n; ++ i)
for (int j = 0; j <= i; ++ j)
{
f[i][j] = f[i - 1][j];
if (j) f[i][j] = (f[i][j] + f[i - 1][j - 1] * (num[i] - j + 1) % mod) % mod;
}
for (int i = n; i >= 1; -- i)
{
g[i] = f[n][i] * fac[n - i] % mod;
for (int j = i + 1; j <= n; ++j)
g[i] = (g[i] - g[j] * C[j][i] % mod + mod) % mod;
}
if ((K ^ n) & 1) cout << "0" << endl;
else if (K == 0) cout << g[n / 2] << endl;
else cout << (g[(n + K) / 2] + g[(n - K) / 2]) % mod << endl;
return 0;
}
总结:
需要先搞好dp的转移的顺序,还有的就是要认真模拟调试一下,看数据的变化是否在计算范围之内。
标签:second bit 个人 决策 min can its 总结 end
原文地址:http://www.cnblogs.com/xgtao/p/6021062.html