【题目】UVA-10559 方块消除

本文解析了一款消除方块游戏的算法实现,通过定义状态f[i][j][k]来计算消除最大得分,探讨了状态转移方程,并提供了两种实现方式:记忆化搜索和动态规划。代码示例清晰展示了算法细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

nnn个带颜色的方块排成一排,相同颜色的方块连成一段同色区域,如下图所示:

游戏时,玩家可以任选一段同色区域,将其消去。设消去的这段包含xxx个相同颜色的方块,则此次消除操作的得分为x2x^2x2。然后右边的所有方块会往左边合拢。如下图所示:

对于给定的一排方块,计算消除它们能得到的最大得分。
原题目有ttt组数据(t⩽15t\leqslant 15t15),每组数据n⩽200n\leqslant 200n200


思路

不知道为什么我们就定了一个状态f[i][j][k]f[i][j][k]f[i][j][k]。设第iii个方块的颜色为a[i]a[i]a[i],则f[i][j][k]f[i][j][k]f[i][j][k]表示区间[l,r][l,r][l,r]右侧有kkk个与a[r]a[r]a[r]同色的方块时,将区间[l,r+k][l,r+k][l,r+k]全部消除的最大得分。
转移分两种:

  1. 既然右侧有k+1k+1k+1个同色的方块,可以把区间[l,r+k][l,r+k][l,r+k]右侧所有同色方块找出来,一并消除。具体实现是找到尽可能长的与a[r]a[r]a[r]同色的区间[p,r][p,r][p,r],消除的得分为(r+k−p+1)2(r+k-p+1)^2(r+kp+1)2
    f[l][r][k]←f[l][p−1][0]+(r+k−p+1)2f[l][r][k]\leftarrow f[l][p-1][0]+(r+k-p+1)^2f[l][r][k]f[l][p1][0]+(r+kp+1)2
  2. 还有可能出现的情况是,一段区间被消除后,右边的方块恰好与左边的方块同色,合拢后构成了一段更长的同色区间。若ppp位置的左侧还有与a[r]a[r]a[r]同色的区间,就可能出现上述情况,于是我们讨论消除中间的哪一段区间。枚举同色区间的右端点qqq,考虑消除区间[q+1,p−1][q+1,p-1][q+1,p1]中的所有方块。
    f[i][j][k]←f[l][q][r+k−p+1]+f[q+1][p−1][0]f[i][j][k]\leftarrow f[l][q][r+k-p+1]+f[q+1][p-1][0]f[i][j][k]f[l][q][r+kp+1]+f[q+1][p1][0]
    其实还有消除其中多段不同色区间的情况。设区间[p,r+k][p,r+k][p,r+k]左侧还有www个与a[r]a[r]a[r]同色的区间,右端点分别为q1,q2,…,qwq_1,q_2,\dots,q_wq1,q2,,qw。此时存在一种方案,把这些同色区间之间的不同色区间先全部消除,然后剩下的w+1w+1w+1个同色区间拼在一起一并消除。这种情况是包含在消除区间[qw+1,p−1][q_w+1,p-1][qw+1,p1]的情况中的,此时最右边的两个同色区间合并,变成了状态f[l][qw−1][r+k−p+1]f[l][q_{w-1}][r+k-p+1]f[l][qw1][r+kp+1],所以可以被讨论到。

推荐记忆化搜索实现。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200+5;
int a[maxn],n,t,kase=0;
int f[maxn][maxn][maxn];
bool vis[maxn][maxn][maxn];
int dp_dfs(int l,int r,int k){
  if(l>r)return 0;
  if(vis[l][r][k])return f[l][r][k];
  int q,p=r;vis[l][r][k]=1;
  while(p>l&&a[r]==a[p-1])p--;
  f[l][r][k]=dp_dfs(l,p-1,0)+(r+k-p+1)*(r+k-p+1);
  for(q=l;q<p;q++)if(a[q]==a[r]&&a[q]!=a[q+1])
    f[l][r][k]=max(f[l][r][k],dp_dfs(l,q,r+k-p+1)+dp_dfs(q+1,p-1,0));
  return f[l][r][k];
}
int main(){
  scanf("%d",&t);
  while(t--){
    scanf("%d",&n);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    printf("Case %d: %d\n",++kase,dp_dfs(1,n,0));
  }
  return 0;
}
思路×2\times 2×2

考试的时候考到原题了。。然而原来的状态推不动。。自己重新定了一个状态。。
首先可以定一个幼稚的状态:f[i][j]f[i][j]f[i][j]表示将区间[i,j][i,j][i,j]消除能得到的最大得分。答案为f[1][n]f[1][n]f[1][n]
考虑到一些不可描述的因素,定义一个辅助状态g[i][j][c]g[i][j][c]g[i][j][c],表示当a[i]=a[j]a[i]=a[j]a[i]=a[j]时,消除区间[i,j][i,j][i,j]内的一些方块,使得这一部分方块还剩下ccc个与a[i]a[i]a[i]同色的方块,能够得到的最大得分。
状态转移:

f[i][j]=max⁡{f[i][k]+f[k+1][j]g[i][j][c]+c2if a[i]=a[j]g[i][j][c]=max⁡{g[i][q][c−(j−p+1)]+f[q+1][p−1]}\begin{aligned} f[i][j]&=\max\begin{cases} f[i][k]+f[k+1][j]\\ g[i][j][c]+c^2&\text{if }a[i]=a[j] \end{cases}\\ g[i][j][c]&=\max\lbrace g[i][q][c-(j-p+1)]+f[q+1][p-1]\rbrace \end{aligned}f[i][j]g[i][j][c]=max{f[i][k]+f[k+1][j]g[i][j][c]+c2if a[i]=a[j]=max{g[i][q][c(jp+1)]+f[q+1][p1]}

第二个式子中p,qp,qp,q的含义跟标解相同。
偏不用记忆化搜索。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200+5;
const int INF=0x3f3f3f3f;
int a[maxn],n,t,kase=0;
int f[maxn][maxn],g[maxn][maxn][maxn];
int main(){
  scanf("%d",&t);
  while(t--){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      for(int j=i;j<=n;j++){
        f[i][j]=-INF;
        for(int k=0;k<=j-i+1;k++)g[i][j][k]=-INF;
    }
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)f[i][i]=1,g[i][i][1]=0;
    for(int t=2;t<=n;t++)
      for(int i=1,j;(j=i+t-1)<=n;i++){
        for(int k=i;k<j;k++)
          f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
        if(a[i]!=a[j])continue;
        int q,p=j,cnt=0;
        while(p>i&&a[p]==a[p-1])p--;
        if(p==i)f[i][j]=max(f[i][j],t*t),g[i][j][t]=0;
        else for(q=i;q<p;q++)if(a[q]==a[j]){
          cnt++;
          if(a[q]!=a[q+1])for(int k=j-p+1;k<=j-p+1+cnt;k++)
            g[i][j][k]=max(g[i][j][k],g[i][q][k-(j-p+1)]+f[q+1][p-1]);
        }
        for(int k=0;k<=t;k++)f[i][j]=max(f[i][j],g[i][j][k]+k*k);
    }
    printf("Case %d: %d\n",++kase,f[1][n]);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值