[GXOI/GZOI2019]宝牌一大堆——DP

题目链接:

[GXOI/GZOI2019]宝牌一大堆

求最大值容易想到$DP$,但如果将$7$种和牌都考虑进来的话,$DP$状态不好设,我们将比较特殊的七小对和国士无双单独求,其他的进行$DP$。

观察其他五种和牌可以发现,他们都是由$4$组杠子或面子和$1$组雀头组成。

那么可以列出$DP$式子:$f[i][j][k][l][m][n]$表示前$i$种牌,其中有$j$个杠子或面子、$k$个雀头,第$i-2\sim i$种牌分别有$l,m,n$张时前$i-3$种牌的最大值。

转移时对顺子、杠子、刻子和雀头四种情况分别转移即可。

对于国士无双,暴力枚举第$14$张牌是什么然后取最大值即可。

对于七小对,设$F[i][j]$表示前$i$种牌中取了$j$种雀头的最大值,$01$背包转移即可。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll f[35][5][2][5][5][5];
ll F[35][8];
char ch[3];
int T;
ll p[6];
ll c[10][10];
int s[35];
int t[35];
bool vis[35]={0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0};
int g[20]={1,9,10,18,19,27,28,29,30,31,32,33,34};
void add(ll &x,ll y)
{
	x=max(x,y);
}
int find(char ch[3])
{
	if(ch[0]=='E')return 28;
	if(ch[0]=='S')return 29;
	if(ch[0]=='W')return 30;
	if(ch[0]=='N')return 31;
	if(ch[0]=='Z')return 32;
	if(ch[0]=='B')return 33;
	if(ch[0]=='F')return 34;
	if(ch[1]=='m')return ch[0]-'0';
	if(ch[1]=='p')return 9+ch[0]-'0';
	if(ch[1]=='s')return 18+ch[0]-'0';
}
ll get(int id,int num)
{
	return t[id]?p[num]:1ll;
}
ll DP()
{
	ll res=0;
	memset(f,0,sizeof(f));
	f[1][0][0][0][0][0]=1ll;
	for(int i=1;i<=34;++i)
	{
		for(int j=0;j<=4;++j)
		{
			for(int k=0;k<=1;++k)
			{
				for(int l=0;l<=4;++l)
				{
					for(int m=0;m<=4;++m)
					{
						for(int n=0;n<=4;++n)
						{
							ll now=f[i][j][k][l][m][n];
							if(!now)continue;
							if(i<34)add(f[i+1][j][k][m][n][0],now*(i>2?c[s[i-2]][l]:1)*get(i-2,l));
							if(j<4&&s[i]-n>=3)add(f[i][j+1][k][l][m][n+3],now);
							if(j<4&&s[i]-n>=4)add(f[i][j+1][k][l][m][n+4],now);
							if(j<4&&vis[i]&&s[i]-n&&s[i-1]-m&&s[i-2]-l)add(f[i][j+1][k][l+1][m+1][n+1],now);
							if(k<1&&s[i]-n>=2)add(f[i][j][k+1][l][m][n+2],now);
							if(i==34&&j==4&&k==1)add(res,now*c[s[i]][n]*c[s[i-1]][m]*c[s[i-2]][l]*get(i,n)*get(i-1,m)*get(i-2,l));
						}
					}
				}
			}
		}
	}
	return res;
}
ll qxd()
{
	memset(F,0,sizeof(F));
	F[0][0]=1ll;
	for(int i=1;i<=34;i++)
	{
		for(int j=0;j<=7;j++)
		{
			if(!F[i-1][j])continue;
			add(F[i][j],F[i-1][j]);
			if(j<7)add(F[i][j+1],F[i-1][j]*c[s[i]][2]*get(i,2));
		}
	}
	return F[34][7]*7;
}
ll gsws()
{
	ll res=0;
	for(int i=0;i<13;i++)
	{
		if(!s[g[i]])return 0;
		if(s[g[i]]==1)continue;
		ll sum=c[s[g[i]]][2]*get(g[i],2);
		for(int j=0;j<13;j++)
		{
			if(i==j)continue;
			sum=sum*s[g[j]]*get(g[j],1);
		}
		add(res,sum*13);
	}
	return res;
}
int main()
{
	for(int i=0;i<=8;i++)
	{
		c[i][0]=1ll;
		for(int j=1;j<=i;j++)
		{
			c[i][j]=c[i-1][j]+c[i-1][j-1];
		}
	}
	p[0]=1ll;
	for(int i=1;i<=4;i++)
	{
		p[i]=p[i-1]*2ll;
	}
	scanf("%d",&T);
	while(T--)
	{
		for(int i=1;i<=34;i++)
		{
			s[i]=4;
		}
		while(1)
		{
			scanf("%s",ch);
			if(ch[0]=='0')break;
			s[find(ch)]--;
		}
		memset(t,0,sizeof(t));
		while(1)
		{
			scanf("%s",ch);
			if(ch[0]=='0')break;
			t[find(ch)]=1;
		}
		ll ans=0;
		add(ans,DP());
		add(ans,gsws());
		add(ans,qxd());
		printf("%lld\n",ans);
	}
}

转载于:https://www.cnblogs.com/Khada-Jhin/p/10717677.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值