首页 > 其他分享 >打砖块 题解

打砖块 题解

时间:2024-06-08 17:13:51浏览次数:14  
标签:背包 int 题解 子弹 分组 pd 砖块 dp

题目链接

\(50 pts\)

对于没有 \(Y\) 砖的情况,可以用分组背包解决,算出每一列打 \(j\) 块砖需要的子弹以及对分数的贡献,按照分组背包即可。

对于包含 \(Y\) 砖的情况,不能直接分组背包解决。这实际上是打的顺序问题,比如:

N Y
N Y

如果手上有两枚子弹,最优策略是先打掉第二列,再打掉第一列;但分组背包的思路是:在打第二列的时候,由于至少需要一个,所以第一列也只能用一枚子弹打,也就是:第一列打了一个,第二列打了两个,显然不优。

\(100pts\)

我们发现,打一个 \(Y\) 砖块的要求和影响分别是:手中必须握有至少一枚子弹,打完后子弹总数没有减少。

可以将列分为最后打的列(显然只有一列)和不是最后打的列,对于不是最后打的列,用 \(k-1\) 枚子弹去打,这时就可以直接跑分组背包,少的那枚子弹藏在手里,当遇到 \(Y\) 砖时需要至少一枚子弹时就拿出来,由于遇到 \(Y\) 砖子弹数不会减少,所以这一枚子弹始终存在。

对于最后打的列:枚举剩下的子弹(加上手中的一枚),按照 \(dp\) 状态和这一列产生的贡献直接计算即可。

那么在枚举最后打的列时,如何快速计算其他列做背包的值呢?可以用前后缀合并的思想做。

设 \(f_{i,j}\) 表示前 \(i\) 列用 \(j\) 子弹的最大分数,\(g_{i,j}\) 表示后 \(i\) 列用 \(j\) 子弹的最大分数。对于二者直接分组背包,时间复杂度 \(O(n^3)\)。

将 \(f_{i-1}\) 和 \(g_{i+1}\) 合并(排除掉第 \(i\) 列)只需枚举 \(f\) 用的子弹数和 \(g\) 用的子弹数,时间复杂度 \(O(n^2)\)。

总结:本解法在非 \(dp\) 的地方进行了转化,从而简化了 \(dp\) 难度。

具体看代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=210;
int n,m,k;
int s[N][N],pd[N][N];
struct node {
    int st,v,w;
}; vector<node> a[N];
int f[N][N],g[N][N],dp[N];

int main(){
    ios::sync_with_stdio(0);
    
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            char c; cin>>s[i][j]>>c;
            if(c=='Y') pd[i][j]=1;
            else pd[i][j]=0;
        }
    }
    for(int j=1;j<=m;j++) {
        int st=0,v=0,w=0;
        for(int i=n;i>=1;i--) {
            v++; w+=s[i][j];
            a[j].push_back({v,v-pd[i][j],w});
            v-=pd[i][j];
        }
    }

    k--;//在手中留一枚子弹
    for(int i=1;i<=m;i++) {//预处理 f
        for(auto t:a[i]) {
            for(int j=0;j<=k;j++) {
                f[i][j]=max(f[i][j],f[i-1][j]);
                if(j>=t.v) f[i][j]=max(f[i][j],f[i-1][j-t.v]+t.w);
            }
        }
    }
    for(int i=m;i>=1;i--) {//预处理 g
        for(auto t:a[i]) {
            for(int j=0;j<=k;j++) {
                g[i][j]=max(g[i][j],g[i+1][j]);
                if(j>=t.v) g[i][j]=max(g[i][j],g[i+1][j-t.v]+t.w);
            }
        }
    }

    int maxn=0;
    for(int i=1;i<=m;i++) {
        memset(dp,0,sizeof(dp));
        for(int j=0;j<=k;j++) {//合并f,g
            for(int z=0;z<=k;z++) {
                if(j+z<=k) dp[j+z]=max(dp[j+z],f[i-1][j]+g[i+1][z]);
            }
        }
        int num=k+1;//将手中的子弹搞回来
        for(int j=0;j<=k;j++) {
            for(auto t:a[i]) {
                if(num-j>=t.st) maxn=max(maxn,dp[j]+t.w);
            }            
        }
    }
    cout<<maxn;
    return 0;
}

标签:背包,int,题解,子弹,分组,pd,砖块,dp
From: https://www.cnblogs.com/zhangyuzhe/p/18238055

相关文章

  • 【Selenium+java环境配置】(超详细教程常见问题解决)
    Selenium+java环境配置windows电脑环境搭建-chrome浏览器1.下载chrome浏览器2.查看chrome浏览器版本3.下载chrome浏览器驱动4.配置系统环境变量PATH验证环境是否搭建成功1.创建java项目,添加pom文件中添加依赖2.编写代码运行常见问题&解决办法1.访问失败Theversio......
  • 猜排列 题解
    猜排列题解差eps步想到正解。题意描述有\(m\)个长为\(n\)序列\(a_1,\dots,a_n\),还有\(m\)个长为\(n\)序列\(b_1,\dots,b_n\)。其中\(b_i\)是由\(a_i\)通过排列\(p\)置换得到的,\(b_{p_i}=a_i\)。现在又要求出排列\(p\)会有多种可能,模\(998244353\)。So......
  • P10466 邻值查找 题解
    题目传送门前置知识二叉搜索树&平衡树解法笔者写这篇题解的时候题面应该是出锅了,建议去看Acwing的题面。第一问同luoguP2234[HNOI2002]营业额统计,平衡树维护前驱、后继(非严格意义上的)求出差值后取\(\min\)即可;第二问用map实现一个映射即可维护位置。代码#incl......
  • P10499 开关问题 题解
    题目传送门前置知识高斯消元法解异或方程组|乘法原理解法把开关的相互影响关系转化成异或,然后就转化成了异或方程组,高斯消元求解即可。判断是否存在解的过程同luoguP2455[SDOI2006]线性方程组。由于自由元仅能取\(0/1\),故总方案数为\(2\)的自由元数量次方。代码......
  • P7219 [JOISC2020] 星座 3 题解
    会发现题目的坐标其实是平面直角坐标系。我们按\(y\)坐标从小到大考虑所有的星星,假设当前考虑到了星星\(i\)。我们先计算出之前所有能够影响到\(i\)的星星的代价和为\(cost\)(可以用树状数组维护)。然后分类讨论。若\(c_i\lecost\),那么肯定直接将\(i\)直接涂黑,因为它更......
  • P10560 [ICPC2024 Xi'an I] The Last Cumulonimbus Cloud 题解
    好好玩的题。思路对于一个图上邻域问题,我们有一个很经典的做法:根号分治。考虑根号分治的本质是什么。我们把点分成两类,平衡每一种点的时间,也就是度数大的与度数小的点。所以对于这道题,我们有了更加好的做法。发现题目给的图的性质就是一个天然的划分方案。我们每次找到图中......
  • P10553 [ICPC2024 Xi'an I] Guess The Tree 题解
    挺有意思的题。思路考虑一个比较自然的做法。我们每次对于一棵树,我们将它的某一条链抽出来。这样,我们只需要知道这颗树的所有节点与链底的\(\text{lca}\),就可以知道它是属于这条链上哪一个节点的下面。然后就可以递归处理。由于交互库不是自适应的。我们可能可以想到随机......
  • P9000 [CEOI2022] Measures 题解
    思路简单题。考虑任意两点之间的限制。任意两点合法时必须要满足:\[\frac{d(j-i)-(a_j-a_i)}{2}\let(i\lej)\]所以答案即为:\[\max_{i\lej}\frac{d(j-i)-(a_j-a_i)}{2}\]使用线段树简单维护即可。时间复杂度:\(O((n+m)\log(n+m))\)。Code#include<bits/stdc++.h>using......
  • CF1192B Dynamic Diameter 题解
    思路静态\(\text{toptree}\)板子题。定义我们使用簇来表示树上的一个连通块。可以按照如下方式定义一个簇:一个簇可以表示为三元组\((u,v,E)\),其中\(u,v\)为树的节点,称为簇的界点,\(E\)为一个边的集合,表示该簇包含的边,路径\((u,v)\)称作簇路径。\(u,v\)分别为上界......
  • 无限之环 题解
    五星压行大师\(lyh\)表示:这是难得能让他的代码长度打破百行大关的题目(182行)。首先,根据科技与狠活,本题可以黑白染色。源点联向白格,黑格连向汇点。发现每个格子都可以连向四个方向,所以可以建立四个点,代表水管连到了上下左右四个方向。设四元组\((x,y,z,p)\)表示水管初始状态......