luogu2668 luogu2540 斗地主(以及增强版)

https://www.luogu.org/problem/P2668

https://www.luogu.org/problem/P2540

做得崩溃,不过幸亏洛谷大大可以下载数据

首先看数据水一点点的

显然本题与花色无关,而且除了顺子,其余的都只和同种牌的出现次数有关(与牌的大小无关)

于是考虑$dp_{i,j,k,l}$表示出现$4$次的牌有$i$张,$3$次的牌有$j$张......时,在不打顺子的情况下,打完牌的最少次数

转移有点多,但是很好想,想清楚了就不会写错

那怎么处理顺子呢?

暴搜跑得还挺快

有两点需要注意:

1.顺子不一定一次性要打完,如3,4,5,6,7,8,8,8,8,9,9,11,11,这个样例若先打出3,4,5,6,7,再打出8,8,8,8,9,9,11,11,比先打出4,5,6,7,8,9要优

2.同样的牌也不一定非要以同样的形式打出,可以拆成一些单牌,如3,3,3,3,4,4,4,5,5,5先打出3,3,3,3,5,5,再打出4,4,4,5为最优解,把5拆开了

然后我们看数据加强版

其实就一个地方不一样:四带二里面“二”不能是双王

给$dp$数组加一维表示王的数量即可

然后转移的时候记得加一些东西

P2668

#include<bits/stdc++.h>
using namespace std;
int n,cnt[20];
int dp[20][20][20][20];
void cmin(int x,int &y) {y=x>y?y:x;}
void prepare()
{
    memset(dp,0x3f,sizeof(dp));
    dp[0][0][0][0]=0;
    for(int i=0;i<=14;++i)
        for(int j=0;j<=14;++j)
            for(int k=0;k<=14;++k)
                for(int l=0;l<=14;++l)
                if(i*4+j*3+k*2+l<=n)
                {
                    if(i>0)
                    {
                    //炸弹
                        cmin(dp[i-1][j][k][l]+1,dp[i][j][k][l]);
                    //四带二
                        if(l>1) cmin(dp[i-1][j][k][l-2]+1,dp[i][j][k][l]);
                        if(k>1) cmin(dp[i-1][j][k-2][l]+1,dp[i][j][k][l]);
                    //拆开打1+1+1+1,1+1+2,2+2,1+3
                        cmin(dp[i-1][j][k][l+4],dp[i][j][k][l]);
                        cmin(dp[i-1][j][k+1][l+2],dp[i][j][k][l]);
                        cmin(dp[i-1][j][k+2][l],dp[i][j][k][l]);
                        cmin(dp[i-1][j+1][k][l+1],dp[i][j][k][l]);
                    }
                    if(j>0)
                    {
                    //三张牌
                        cmin(dp[i][j-1][k][l]+1,dp[i][j][k][l]);
                    //三带一
                        if(l>0) cmin(dp[i][j-1][k][l-1]+1,dp[i][j][k][l]);
                    //三带二
                        if(k>0) cmin(dp[i][j-1][k-1][l]+1,dp[i][j][k][l]);
                    //拆开打1+1+1,1+2
                        cmin(dp[i][j-1][k][l+3],dp[i][j][k][l]);
                        cmin(dp[i][j-1][k+1][l+1],dp[i][j][k][l]);
                    }
                    if(k>0)
                    {
                    //对子与火箭
                        cmin(dp[i][j][k-1][l]+1,dp[i][j][k][l]);
                    //拆开打1+1
                        cmin(dp[i][j][k-1][l+2],dp[i][j][k][l]);
                    }
                    //单张牌
                    if(l>0) cmin(dp[i][j][k][l-1]+1,dp[i][j][k][l]);
                }
}
int dfs(int step,int cow[])
{
    int ex[5]={0,0,0,0,0};
    int now[20];
    for(int i=1;i<=14;++i) now[i]=cow[i];
    for(int i=1;i<=14;++i) ++ex[now[i]];
    if(ex[0]==14) return 0;
    //不打顺子
    int ret=dp[ex[4]][ex[3]][ex[2]][ex[1]]+step;
    //三顺子
    for(int i=1;i<=12;++i)
    {
        int cc=0;
        for(int j=1;i+j-1<=12;++j)
            if(now[i+j-1]>=3) ++cc;
            else break;
        now[i]-=3;
        for(int j=2;j<=cc;++j)
        {
            now[i+j-1]-=3;
            cmin(dfs(step+1,now),ret);
        }
        now[i]+=3;
        for(int j=2;j<=cc;++j) now[i+j-1]+=3;
    }
    //双顺子
    for(int i=1;i<=12;++i)
    {
        int cc=0;
        for(int j=1;i+j-1<=12;++j)
            if(now[i+j-1]>=2) ++cc;
            else break;
        now[i]-=2,now[i+1]-=2;
        for(int j=3;j<=cc;++j)
        {
            now[i+j-1]-=2;
            cmin(dfs(step+1,now),ret);
        }
        now[i]+=2,now[i+1]+=2;
        for(int j=3;j<=cc;++j) now[i+j-1]+=2;
    }
    //单顺子
    for(int i=1;i<=12;++i)
    {
        int cc=0;
        for(int j=1;i+j-1<=12;++j)
            if(now[i+j-1]>=1) ++cc;
            else break;
        for(int j=0;j<4;++j) now[i+j]-=1;
        for(int j=5;j<=cc;++j)
        {
            now[i+j-1]-=1;
            cmin(dfs(step+1,now),ret);
        }
        for(int j=0;j<4;++j) now[i+j]+=1;
        for(int j=5;j<=cc;++j) now[i+j-1]+=1;
    }
    return ret;
}
int main()
{
//    freopen("testdata.in","r",stdin);
//    freopen("testdata.out","w",stdout);
    int T;
    scanf("%d%d",&T,&n);
    prepare();
    while(T--)
    {
        for(int i=1;i<=15;++i) cnt[i]=0;
        for(int i=1;i<=n;++i)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(a>2) ++cnt[a-2];
            else if(a) ++cnt[a+11];
                else ++cnt[14];
        }
        printf("%d\n",dfs(0,cnt));
    }
    return 0;
}

P2540

#include<bits/stdc++.h>
using namespace std;
int n,cnt[20];
int dp[20][20][20][20][3];
void cmin(int x,int &y) {y=x>y?y:x;}
void prepare()
{
    memset(dp,0x3f,sizeof(dp));
    dp[0][0][0][0][0]=0;
    for(int i=0;i<=13;++i)
        for(int j=0;j<=13;++j)
            for(int k=0;k<=13;++k)
                for(int l=0;l<=13;++l)
                    for(int s=0;s<=2;++s)
                if(i*4+j*3+k*2+l+s<=n)
                {
                    if(i>0)
                    {
                    //炸弹
                        cmin(dp[i-1][j][k][l][s]+1,dp[i][j][k][l][s]);
                    //四带二
                        if(l>1) cmin(dp[i-1][j][k][l-2][s]+1,dp[i][j][k][l][s]);
                        if(s>1) cmin(dp[i-1][j][k][l][s-2]+1,dp[i][j][k][l][s]);
                        if(k>1) cmin(dp[i-1][j][k-2][l][s]+1,dp[i][j][k][l][s]);
                        if(s>0 && l>0) cmin(dp[i-1][j][k][l-1][s-1]+1,dp[i][j][k][l][s]);
                    //拆开打1+1+1+1,1+1+2,2+2,1+3
                        cmin(dp[i-1][j][k][l+4][s],dp[i][j][k][l][s]);
                        cmin(dp[i-1][j][k+1][l+2][s],dp[i][j][k][l][s]);
                        cmin(dp[i-1][j][k+2][l][s],dp[i][j][k][l][s]);
                        cmin(dp[i-1][j+1][k][l+1][s],dp[i][j][k][l][s]);
                    }
                    if(j>0)
                    {
                    //三张牌
                        cmin(dp[i][j-1][k][l][s]+1,dp[i][j][k][l][s]);
                    //三带一
                        if(l>0) cmin(dp[i][j-1][k][l-1][s]+1,dp[i][j][k][l][s]);
                        if(s>0) cmin(dp[i][j-1][k][l][s-1]+1,dp[i][j][k][l][s]);
                    //三带二
                        if(k>0) cmin(dp[i][j-1][k-1][l][s]+1,dp[i][j][k][l][s]);
                    //拆开打1+1+1,1+2
                        cmin(dp[i][j-1][k][l+3][s],dp[i][j][k][l][s]);
                        cmin(dp[i][j-1][k+1][l+1][s],dp[i][j][k][l][s]);
                    }
                    if(k>0)
                    {
                    //对子
                        cmin(dp[i][j][k-1][l][s]+1,dp[i][j][k][l][s]);
                    //拆开打1+1
                        cmin(dp[i][j][k-1][l+2][s],dp[i][j][k][l][s]);
                    }
                    //单张牌
                    if(l>0) cmin(dp[i][j][k][l-1][s]+1,dp[i][j][k][l][s]);
                    //火箭
                    if(s>1) cmin(dp[i][j][k][l][s-2]+1,dp[i][j][k][l][s]);
                    if(s>0) cmin(dp[i][j][k][l][s-1]+1,dp[i][j][k][l][s]);
                }
}
int dfs(int step,int cow[])
{
    int ex[5]={0,0,0,0,0};
    int now[20];
    for(int i=1;i<=14;++i) now[i]=cow[i];
    for(int i=1;i<=13;++i) ++ex[now[i]];
    //不打顺子
    int ret=dp[ex[4]][ex[3]][ex[2]][ex[1]][now[14]]+step;
    //三顺子
    for(int i=1;i<=12;++i)
    {
        int cc=0;
        for(int j=1;i+j-1<=12;++j)
            if(now[i+j-1]>=3) ++cc;
            else break;
        now[i]-=3;
        for(int j=2;j<=cc;++j)
        {
            now[i+j-1]-=3;
            cmin(dfs(step+1,now),ret);
        }
        now[i]+=3;
        for(int j=2;j<=cc;++j) now[i+j-1]+=3;
    }
    //双顺子
    for(int i=1;i<=12;++i)
    {
        int cc=0;
        for(int j=1;i+j-1<=12;++j)
            if(now[i+j-1]>=2) ++cc;
            else break;
        now[i]-=2,now[i+1]-=2;
        for(int j=3;j<=cc;++j)
        {
            now[i+j-1]-=2;
            cmin(dfs(step+1,now),ret);
        }
        now[i]+=2,now[i+1]+=2;
        for(int j=3;j<=cc;++j) now[i+j-1]+=2;
    }
    //单顺子
    for(int i=1;i<=12;++i)
    {
        int cc=0;
        for(int j=1;i+j-1<=12;++j)
            if(now[i+j-1]>=1) ++cc;
            else break;
        for(int j=0;j<4;++j) now[i+j]-=1;
        for(int j=5;j<=cc;++j)
        {
            now[i+j-1]-=1;
            cmin(dfs(step+1,now),ret);
        }
        for(int j=0;j<4;++j) now[i+j]+=1;
        for(int j=5;j<=cc;++j) now[i+j-1]+=1;
    }
    return ret;
}
int main()
{
//    freopen("testdata.in","r",stdin);freopen("testdata.out","w",stdout);
    int T;
    scanf("%d%d",&T,&n);
    prepare();
    while(T--)
    {
        for(int i=1;i<=15;++i) cnt[i]=0;
        for(int i=1;i<=n;++i)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(a>2) ++cnt[a-2];
            else if(a) ++cnt[a+11];
                else ++cnt[14];
        }
        printf("%d\n",dfs(0,cnt));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/w19567/p/11279643.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值