13662:Sequential Yahtzee[状压+dp+模拟]

题意

n个数字,代表每一次掷骰子的点数,每轮掷5个,有两次机会,从中挑选任意个重新掷。从第一个骰子开始,共进行13轮,按照顺序放入13个格子中,每个格子按照图中计分,求可能达到的最高得分。

思路

首先处理一轮的得分,枚举每一个左端点,模拟掷的过程(利用状压枚举两次,枚举到的骰子替换掉,得到保留的5个骰子以及左端点、右端点(5个骰子中最大的那个值)。得到骰子的所有状态以后,对每一个状态,尝试填入13个格子中,按照格子内的规则更新答案。
这部分处理的复杂度是 O ( n ∗ 2 5 ∗ 2 5 ∗ 13 × c h e c k 复 杂 度 ) O(n*2^5*2^5*13×check复杂度) O(n252513×check)
然后是check,按照规则判就可以了,相应的对答案进行更新(取max)。注意如果check的格子不满足条件(比如葫芦格子是五个单独的要用0更新,与无法到达的负数进行区分。
然后用dp进行区间合并,用dp[r][k]代表左端点为1,右端点为r,已经填入前k个格子时的得分,枚举mid,可以用dp[mid][k-1]+base[mid+1][r][k]更新当前答案,时间复杂度 O ( n 2 m ) O(n^2m) O(n2m)
由于可以不用掷完所有骰子,所以需要扫一遍k=13的dp数组,取最大值计作答案。
注意dp数组和base数组需要初始化为一个比较大的负数,如果是0会导致从中间开始计算答案而wa掉

#include <bits/stdc++.h>
#define rint register int
typedef long long ll;
using namespace std;
const int N=200+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-14;
const ll zzz=1e9;
struct ios{
    inline char gc(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template<typename _Tp> inline ios&operator>>(_Tp&x){
        static char ch,sgn;ch=gc(),sgn=0;
        for(;!isdigit(ch);ch=gc()){if(ch==-1)return *this;sgn|=ch=='-';}
        for(x=0;isdigit(ch);ch=gc()) x=x*10+(ch^'0');
        sgn&&(x=-x);
    }
}io;
int a[N];
int base[N][N][20],dp[N][20];
int now[20],pre1[20],pre2[20];
int cnt[20];
void upd(int &a,int b)
{
    if(b>a) a=b;
}
void judge(int type,int l,int r)
{
    upd(base[l][r][type],0);
    if(type<=6)
    {
        int c=0;
        for(int i=0;i<5;i++)
            if(type==a[now[i]]) c++;
        upd(base[l][r][type],c*type);
        return;
    }
    int sum=0,mx=0;
    memset(cnt,0,sizeof cnt);
    for(int i=0;i<5;i++)
    {
        sum+=a[now[i]];
        cnt[a[now[i]]]++,upd(mx,cnt[a[now[i]]]);
    }
    if((type==7&&mx>=3)||(type==8&&mx>=4)||type==12)
    {
        upd(base[l][r][type],sum);
        return;
    }
    if(type==13)
    {
        if(mx==5) upd(base[l][r][type],50);
        return;
    }
    if(type==9)
    {
        if(mx!=3) return;
        for(int i=1;i<=6;i++)
            if(cnt[i]==2)
            {
                upd(base[l][r][type],25);
                return;
            }
        return;
    }
    if(type==10)
    {
        for(int i=1;i<=3;i++)
        {
            if(cnt[i]&&cnt[i+1]&&cnt[i+2]&&cnt[i+3])
            {
                upd(base[l][r][type],30);
                return;
            }
        }
        return;
    }
    if(type==11)
    {
        for(int i=1;i<=2;i++)
        {
            if(cnt[i]&&cnt[i+1]&&cnt[i+2]&&cnt[i+3]&&cnt[i+4])
            {
                upd(base[l][r][type],40);
                return;
            }
        }
        return;
    }
}
void gao(int l,int r)
{
    for(int i=0;i<(1<<5);i++)
    {
        for(int j=0;j<5;j++)
        {
            pre1[j]=now[j];
            if(i&(1<<j)) now[j]=r++;
        }
        for(int ii=0;ii<(1<<5);ii++)
        {
            for(int jj=0;jj<5;jj++)
            {
                pre2[jj]=now[jj];
                if(ii&(1<<jj)) now[jj]=r++;
            }
            for(int k=1;k<=13;k++)
            {
                judge(k,l,r-1);
            }
            for(int jj=0;jj<5;jj++)
            {
                if(now[jj]!=pre2[jj]) r--;
                now[jj]=pre2[jj];
            }
        }
        for(int j=0;j<5;j++)
        {
            if(now[j]!=pre1[j]) r--;
            now[j]=pre1[j];
        }
    }
}
int main()
{
    memset(dp,0xbf,sizeof dp);
    for(int i=1;i<N;i++)
    for(int j=1;j<N;j++)
        for(int k=1;k<=13;k++)
        base[i][j][k]=-12345;

    int n;
    io>>n;
    for(int i=1;i<=n;i++)
        io>>a[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<i+5;j++)
            now[j-i]=j;
        gao(i,i+5);
    }


    for(int r=1;r<=n;r++)
        upd(dp[r][1],base[1][r][1]);
    for(int k=1;k<=13;k++)
        for(int r=10;r<=n;r++)
            for(int mid=5;mid<=r-5;mid++)
            {
                upd(dp[r][k],dp[mid][k-1]+base[mid+1][r][k]);
            }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,dp[i][13]);
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值