洛谷 2668&2540 斗地主——搜索+贪心+dp

题目:https://www.luogu.org/problemnew/show/P2540

发现如果没有顺子,剩下的可以贪心。所以搜索顺子怎么出,然后贪心。

这样只能过不加强版。原因是贪心的时候难以弄3=1+2。3应该是 3带* 还是拆开让4带上?

如这个数据(×后面是个数):3×3,4×1,6×4,7×3,9×1,10×2,11×1,12×4,13×3

  正解应该是把一个3拆成1+2,然后两次4带2,两次3带2。但贪心似乎做不了。

所以应该dp!记录1,2,3,4,王各有几个,就能把“拆”体现在状态转移里了。

自己以“还剩几张牌”为阶段,“拆”就是同层转移了;所以需要注意枚举的顺序,保证先同层转移到自己,再从自己转移。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=30,M=20;
int T,n,a[N][M],nm[5],dp[N][N>>1][N/3][N>>2][3],fg,ans;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
void Mn(int &x,int y){x=min(x,y);}
void calc(int cr)
{
  memset(dp,0x3f,sizeof dp); memset(nm,0,sizeof nm);
  for(int i=1;i<=13;i++)nm[a[cr][i]]++;
  dp[nm[1]][nm[2]][nm[3]][nm[4]][fg]=0;
  int lm1=n,lm2=n>>1,lm3=n/3,lm4=(n>>2);
  for(int sm=nm[1]+(nm[2]<<1)+nm[3]*3+(nm[4]<<2)+fg;sm>=0;sm--)
    for(int l=lm4;l>=0;l--)
      for(int k=lm3;k>=0;k--)
    for(int j=lm2;j>=0;j--)
      for(int t=0;t<=2;t++)
        {
          int i=sm-(j<<1)-k*3-(l<<2)-t; if(i<0)continue;
          int d=dp[i][j][k][l][t]; if(d>n-cr)continue;
          if(l)Mn(dp[i+1][j][k+1][l-1][t],d);//4=1+3
          if(l)Mn(dp[i][j+2][k][l-1][t],d);//4=2+2
          if(k)Mn(dp[i+1][j+1][k-1][l][t],d);//3=1+2
          if(j)Mn(dp[i+2][j-1][k][l][t],d);//2=1+1

          d++;
          if(l&&i>=2)Mn(dp[i-2][j][k][l-1][t],d);
           if(l&&i&&t)Mn(dp[i-1][j][k][l-1][t-1],d);
          if(l&&t>=2)Mn(dp[i][j][k][l-1][0],d);
          if(l&&j>=2)Mn(dp[i][j-2][k][l-1][t],d);

          if(k&&j)Mn(dp[i][j-1][k-1][l][t],d);
          if(k&&i)Mn(dp[i-1][j][k-1][l][t],d);
          if(k&&t)Mn(dp[i][j][k-1][l][t-1],d);

          if(i)Mn(dp[i-1][j][k][l][t],d);
          if(j)Mn(dp[i][j-1][k][l][t],d);
          if(k)Mn(dp[i][j][k-1][l][t],d);
          if(l)Mn(dp[i][j][k][l-1][t],d);
          if(t==1)Mn(dp[i][j][k][l][t-1],d);
          if(t==2)Mn(dp[i][j][k][l][0],d);
        }
  ans=min(ans,cr+dp[0][0][0][0][0]);
}
void solve(int cr)
{
  if(cr>ans)return;
  memcpy(a[cr],a[cr-1],sizeof a[cr-1]);
  for(int i=1,j;i<=11;i++)
    if(a[cr][i]>=3&&a[cr][i+1]>=3)
      {
    a[cr][i]-=3;
    for(j=i+1;j<=12&&a[cr][j]>=3;j++)
      a[cr][j]-=3;
    for(j--;j>=i+1;j--) solve(cr+1),a[cr][j]+=3;
    a[cr][i]+=3;
      }
  for(int i=1,j;i<=10;i++)
    if(a[cr][i]>=2&&a[cr][i+1]>=2&&a[cr][i+2]>=2)
      {
    a[cr][i]-=2;a[cr][i+1]-=2;
    for(j=i+2;j<=12&&a[cr][j]>=2;j++)
      a[cr][j]-=2;
    for(j--;j>=i+2;j--) solve(cr+1),a[cr][j]+=2;
    a[cr][i+1]+=2;a[cr][i]+=2;
      }
  for(int i=1,j;i<=8;i++)
    if(a[cr][i]&&a[cr][i+1]&&a[cr][i+2]&&a[cr][i+3]&&a[cr][i+4])
      {
    a[cr][i]--;a[cr][i+1]--;a[cr][i+2]--;a[cr][i+3]--;
    for(j=i+4;j<=12&&a[cr][j];j++)
      a[cr][j]--;
    for(j--;j>=i+4;j--) solve(cr+1),a[cr][j]++;
    a[cr][i+3]++;a[cr][i+2]++;a[cr][i+1]++;a[cr][i]++;
      }
  calc(cr-1);
}
int main()
{
  T=rdn(); n=rdn();
  while(T--)
    {
      ans=N;
      memset(a[0],0,sizeof a[0]); fg=0;//!
      for(int i=1,u,v;i<=n;i++)
    {
      u=rdn(); v=rdn();
      if(u)a[0][u<=2?u+11:u-2]++;
      else fg++;
    }
      solve(1);
      printf("%d
",ans);
    }
  return 0;
}
原文地址:https://www.cnblogs.com/Narh/p/9771346.html