• 向再见说再见


    题意:

    有一场比赛,有两支队伍,每支队伍有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的转移的顺序,还有的就是要认真模拟调试一下,看数据的变化是否在计算范围之内。

     

  • 相关阅读:
    PDA智能程序访问WebService,报告“未能建立与网络的连接”
    VS2008中开发智能设备程序的一些总结收藏
    Error: The INF file contains Unicode characters that could not be converted correctly
    在vs2008工程中制作cab包
    linux专题三之如何悄悄破解root密码(以redhat7.2x64为例)
    linux专题一之文件描述符、重定向、管道符、tee命令
    linux的计划
    如何安装RHEL7.2x64 即红帽7.2虚拟机?
    快速排序及查找第K个大的数。
    来来来,做道题,一起防老年痴呆
  • 原文地址:https://www.cnblogs.com/xgtao/p/6021062.html
Copyright © 2020-2023  润新知