可能中奖?一定中奖 容斥原理 动态规划

可能中奖?一定中奖

题目描述

一张彩票上印有2*K个数字,每个数字都是0至9中的某一个,只要满足如下两个条件的任意一个,那么彩票就是“可能中奖彩票”:
1、彩票前K个数字的总和等于最后K个数字的总和。
2、下标是奇数的所有数字的总和 必须等于 下标是偶数的所有数字的总和。

给出数组:lucky[1…n],其中 1 <=n <= 10, 0<=lucky[i]<=9,每一个lucky[i]都是“幸运数字”,lucky[i]互不相同。
如果一张彩票是“可能中奖彩票”,而且该彩票的每一个数字都是“幸运数字”,那么该彩票就是“一定中奖彩票”。
问题是:总共有多少张不同的“一定中奖彩票”? 答案模999983。 请记住,“一定中奖彩票”可能包含前导零。

输入格式

多组测试数据。
第一行,一个整数G,表示有G组测试数据。 1 <= G <= 6
每组测试数据格式:
第一行,两个整数,K和n 。1 <= K <= 50, 1<=n<=10。
第二行,n个整数,第i个整数是lucky[i]。 0<=lucky[i]<=9, lucky[i]互不相同。

输出格式

共G行,每行一个整数。

输入样例

6
1 10
0 1 2 3 4 5 6 7 8 9 
2 2
2 1 
2 10
0 1 2 3 4 5 6 7 8 9 
10 3
7 3 1 
50 10
0 1 2 3 4 5 6 7 8 9 
46 8
2 8 7 5 9 6 0 4

输出样例

10
8
1240
207444
367584
537052

解题思路

题目大意:给出一个lucky数组,求有多少张“一定中奖彩票”。

从题目中我们知道,目标数列符合以下条件:

  1. 2 × K 2\times K 2×K个数字
  2. 2 × K 2\times K 2×K个数字必须是lucky数组中的任意一个
  3. 彩票前K个数字总和等于最后K个数字总和
    或者
    下标是奇数的所有数字的总和 必须等于 下标是偶数的所有数字的总和

我们根据容斥原理可以得知
满足第3个条件的第1个条件以及其它必备条件为种类A
满足第3个条件的第2个条件以及其它必备条件为种类B
满足第3个条件当中的所有条件以及其它必备条件为种类C
A n s = A + B − C Ans=A+B-C Ans=A+BC

A A A怎么求呢?动态规划


我们先将这 2 × K 2\times K 2×K个数字分成2部分,前 K K K个为一部分,后 K K K个为一部分
求出前一部分总和为 i i i的方案数 F i F_i Fi,那么根据乘法原理两个部分总和为 i i i的的方案数就是 F i × F i 即 F i 2 F_i\times F_i即F_i^2 Fi×FiFi2
方案数

所有总和情况的方案数就是 ∑ i = 1 t o t F i × F i \sum_{i=1}^{tot}F_i\times F_i i=1totFi×Fi
f [ i ] [ j ] f[i][j] f[i][j]表示当前选了lucky数组里的 i i i个数字,总和为 j j j的方案数
f [ i ] [ j ] = ∑ k = 1 n f [ i − 1 ] [ j − l u c k y [ k ] ] f[i][j]=\sum_{k=1}^{n}f[i-1][j-lucky[k]] f[i][j]=k=1nf[i1][jlucky[k]]
f [ i ] [ j ] f[i][j] f[i][j]代入总求和式子
∑ i = 1 t o t f [ K ] [ i ] × f [ K ] [ i ] \sum_{i=1}^{tot}f[K][i]\times f[K][i] i=1totf[K][i]×f[K][i]


B B B的方法与求 A A A的一样,为什么呢?
方案数2

好了,到这一步我们要求 C C C情况的方案数


方案数C
这时我们利用刚刚求出的 f f f,枚举 s u m 1 , s u m 2 sum1,sum2 sum1,sum2
K K K个奇数位置:1,3对应了 f [ K − K / 2 ] [ s u m 1 ] f[K-K/2][sum1] f[KK/2][sum1]
K K K个偶数位置:2,4对应 f [ K / 2 ] [ s u m 2 ] f[K/2][sum2] f[K/2][sum2]
K K K个奇数位置:7,9对应 f [ K / 2 ] [ s u m 2 ] f[K/2][sum2] f[K/2][sum2]
K K K个偶数位置:6,8对应了 f [ K − K / 2 ] [ s u m 1 ] f[K-K/2][sum1] f[KK/2][sum1]
结果就是 f [ K − K / 2 ] [ s u m 1 ] × f [ K / 2 ] [ s u m 2 ] × f [ K − K / 2 ] [ s u m 1 ] × f [ K / 2 ] [ s u m 2 ] f[K-K/2][sum1]\times f[K/2][sum2]\times f[K-K/2][sum1]\times f[K/2][sum2] f[KK/2][sum1]×f[K/2][sum2]×f[KK/2][sum1]×f[K/2][sum2]


本题终于完成。


代码

/*
条件:
1、彩票前K个数字的总和等于最后K个数字的总和。
2、下标是奇数的所有数字的总和   必须等于  下标是偶数的所有数字的总和。
3、而且该彩票的每一个数字都是“幸运数字”

同时满足1,3的情况为A
同时满足2,3的情况为B
同时满足1,2,3的情况为C

根据容斥原理,可知
答案=A+B-C

A,B的求法:往K个空位里填n个幸运数字,用动态规划求解
C的求法:在2*K个数字里,将这些数字根据条件1,2分成几组
例如当K=5时,枚举sum1,sum2
前K个奇数位置:1,3,5对应了f[K-K/2][sum1]
前K个偶数位置:2,4对应f[K/2][sum2]
后K个奇数位置:7,9对应f[K/2][sum2]
后K个偶数位置:6,8,10对应了f[K-K/2][sum1]
将这4种情况相乘,得出c
求和sum1*sum2种相同c情况得到C
*/
#include<bits/stdc++.h>

using namespace std;
int G,K,n;
long long f[55][500],lucky[20];
long long ans;

int main()
{
    
    
	freopen("2788.in","r",stdin);
	freopen("2788.out","w",stdout);
     cin>>G;
	 for(int gr=1;gr<=G;gr++)
	 {
    
    
		 cin>>K>>n;
		 ans=0;
		 memset(f,0,sizeof(f));
		 for(int i=1;i<=n;i++)
		 {
    
    
			cin>>lucky[i];
			f[1][lucky[i]]=1;
			if(lucky[i]==0)//当幸运数字里有0时,选K个幸运数字,和为0的情况只有1种
			{
    
    
				for(int j=0;j<=K;j++)
					f[j][0]=1;
			}
		 }
		 for(int i=2;i<=K;i++)
		 {
    
    
			 for(int j=1;j<=450;j++)
				for(int l=1;l<=n;l++)
				 {
    
    
					 if(j>=lucky[l])
					 {
    
    
						f[i][j]+=f[i-1][j-lucky[l]]%999983;//dp求方案数
						f[i][j]%=999983;
					 }
				 }
		 }
		 for(int j=0;j<=450;j++)
		 {
    
    
			 ans+=(f[K][j]*f[K][j])%999983;//求出A
			 ans%=999983;
			 ans+=(f[K][j]*f[K][j])%999983;//求出B
			 ans%=999983;
		 }                                                     
		 for(int i=0;i<=450;i++)//枚举sum1
		 {
    
    
			 for(int j=0;j<=450;j++)//枚举sum2
				 {
    
    
					 long long a=f[K/2][i]*f[K-K/2][j]%999983;
					 ans-=(a*a)%999983;
					 if(ans<0)//当答案出现负数时,加上模数确保答案正确
						ans+=999983;
					 ans%=999983;
				 }
		 }
		 cout<<ans<<endl;
	 }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/bell041030/article/details/89335868
今日推荐