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


    题目传送门(内部题91)


    输入格式

      第一行一个正整数$N$,表示颜色种类数。
      第二行$N$个正整数$k[i],k[i]$表示第$i$种颜色的数量$(1leqslant 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$,但是不会爆$ ext{__}int128$。

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

    时间复杂度:$Theta(13^3 imes 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++

  • 相关阅读:
    linq获取最大ID值
    asp:MultiView选项卡控件,可以用来选择性的显示需要的部门
    TFS修改工作区映射区
    怎么解决javascript小数相减会出现一长串的小数位数?
    (转)向页面动态载入用户控件和自定义控件的方法(谨记)
    (转)工作经验到底是个什么东东?工作经验从哪里来?
    hdu 5441 travel 离线+带权并查集
    hdu 5438 Ponds dfs
    hdu 5437 Alisha’s Party 模拟 优先队列
    CF 500 B. New Year Permutation 并查集
  • 原文地址:https://www.cnblogs.com/wzc521/p/11747076.html
Copyright © 2020-2023  润新知