码迷,mamicode.com
首页 > 其他好文 > 详细

[CSP-S模拟测试]:彩球问题(记忆化搜索)

时间:2019-10-27 13:04:48      阅读:68      评论:0      收藏:0      [点我收藏+]

标签:动态   组合   记忆化搜索   amp   turn   com   set   问题   技术   

题目传送门(内部题91)


输入格式

  第一行一个正整数$N$,表示颜色种类数。
  第二行$N$个正整数$k[i],k[i]$表示第$i$种颜色的数量$(1\leqslant k[i]\leqslant 3)$。


输出格式

  一个整数,表示相同颜色的小球不相邻的方案数。


样例

样例输入1:

3
1 2 3

样例输出1:

10

样例输入2:

4
1 3 2 1

样例输出2:

96


数据范围与提示

输入的所有数字均为正整数。

技术图片


题解

正解(组合数学$+$容斥原理$+$高精度计算$+$动态规划)好麻烦,我不会……

于是就想到了记忆化搜索……

考场上想的是一个$13$维的$DP$……

定义$dp[lst][res1][res2][res3][res4][res5][res6][res7][res8][res9][res10][res11][res12]$分别表示上一位是$lst$,小球$i$还有$res_i$个的方案数……

就为了比别人多骗十分……

再来考虑正解。

设$f[x][i][j][k]$分别上一次放的小球出现的次数为$x$,表示个数为$1$的小球有$i$个,个数为$2$的小球有$j$个,个数为$3$的小球有$k$个。

然后记忆话搜索就好了。

注意可能会爆$long\ long$,但是不会爆$\text{__}int128$。

数据范围中小球的个数可以到$4\sim 5$。

时间复杂度:$\Theta(13^3\times 3)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const long long mod=1e18;
int n;
int k[4];
__int128 dp[3][13][13][13];
__int128 dfs(int lst,int res1,int res2,int res3)
{
	if(dp[lst][res1][res2][res3]!=-1)return dp[lst][res1][res2][res3];
	dp[lst][res1][res2][res3]=0;
	if(lst==0)
	{
		if(res1)dp[lst][res1][res2][res3]+=res1*dfs(0,res1-1,res2,res3);
		if(res2)dp[lst][res1][res2][res3]+=res2*dfs(1,res1+1,res2-1,res3);
		if(res3)dp[lst][res1][res2][res3]+=res3*dfs(2,res1,res2+1,res3-1);
	}
	if(lst==1)
	{
		if(res1>1)dp[lst][res1][res2][res3]+=(res1-1)*dfs(0,res1-1,res2,res3);
		if(res2)dp[lst][res1][res2][res3]+=res2*dfs(1,res1+1,res2-1,res3);
		if(res3)dp[lst][res1][res2][res3]+=res3*dfs(2,res1,res2+1,res3-1);
	}
	if(lst==2)
	{
		if(res1)dp[lst][res1][res2][res3]+=res1*dfs(0,res1-1,res2,res3);
		if(res2>1)dp[lst][res1][res2][res3]+=(res2-1)*dfs(1,res1+1,res2-1,res3);
		if(res3)dp[lst][res1][res2][res3]+=res3*dfs(2,res1,res2+1,res3-1);
	}
	return dp[lst][res1][res2][res3];
}
int main()
{
	memset(dp,-1,sizeof(dp));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		k[x]++;
	}
	dp[0][0][0][0]=dp[1][0][0][0]=dp[2][0][0][0]=1;
	dfs(0,k[1],k[2],k[3]);
	if(dp[0][k[1]][k[2]][k[3]]>mod)printf("%lld",(long long)(dp[0][k[1]][k[2]][k[3]]/mod));
	printf("%lld",(long long)(dp[0][k[1]][k[2]][k[3]]%mod));
	return 0;
}

rp++

[CSP-S模拟测试]:彩球问题(记忆化搜索)

标签:动态   组合   记忆化搜索   amp   turn   com   set   问题   技术   

原文地址:https://www.cnblogs.com/wzc521/p/11747076.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!