主要的收获是。。如何优化你递推式里面不必要的决策

之前的代码

这个代码在HDU超时了,这就对了。。这个复杂度爆炸。。

但是这个思路非常地耿直。。那就是只需要暴力枚举删两个和删三个的情况,于是就非常耿直的枚举是哪两个n^2,是哪三个n^3

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring> using namespace std;
int T,n,m;
int a[305],d[305];
bool f[305][305];
//定向从左往右删除
int dp[305][305];
int dfs(int l,int r){
// printf("l%d r%d\n",l,r);
if(dp[l][r]!=-1) return dp[l][r];
if(l>=r) return dp[l][r]=0;
int i,j,k,p=0;
//枚举删两个
for(i=l;i<r;++i)
for(j=i+1;j<=r;++j)
{
//删i,j
//如果dfs(x,y)==y-x+1,则说明[x,y]能被完全删除
// printf("part:: i%d j%d\n",i,j);
if(f[i][j]&&(dfs(i+1,j-1)==j-i-1)){
// printf("Tpart:: i%d j%d\n",i,j);
p=max(p,(j-i+1)+dfs(l,i-1)+dfs(j+1,r));
// printf("VAL:: %d\n",p);
}
}
//枚举删三个
for(i=l;i<r;++i)
for(j=i+1;j<r;++j)
for(k=j+1;k<=r;++k)
{
// printf("part:: i%d j%d k%d\n",i,j,k);
if(f[i][j]&&f[j][k]&&(a[j]-a[i]==a[k]-a[j])&&(dfs(i+1,j-1)==j-i-1)&&(dfs(j+1,k-1)==k-j-1)){
// printf("Tpart:: i%d j%d k%d\n",i,j,k);
p=max(p,(k-i+1)+dfs(l,i-1)+dfs(k+1,r));
// printf("VAL:: %d\n",p);
}
}
return dp[l][r]=p;
}
void solve(){
memset(dp,0,sizeof(dp));
int l,r,i,j,k,len;
for(len=2;len<=n;++len){
for(l=1;l<n;++l){
r=l+len-1;
printf("DP l%d r%d\n",l,r);
if(l>=r) continue;
for(i=l;i<r;++i){
for(j=i+1;j<=r;++j){
printf("part2 ASK (%d,%d) (%d,%d)\n",l,i-1,j+1,r);
if(f[i][j]&&dp[i+1][j-1]==j-i-1) {
// printf("part2 ask (%d,%d) (%d,%d)\n",l,i-1,j+1,r);
dp[l][r]=max(dp[l][r],(j-i+1)+dp[l][i-1]+dp[j+1][r]);
}
}
}
for(i=l;i<r;++i){
for(j=i+1;j<r;++j){
for(k=j+1;k<=r;++k){
printf("part3 ASK (%d,%d) (%d,%d)\n",l,i-1,k+1,r);
if(f[i][j]&&f[j][k]&&(a[j]-a[i]==a[k]-a[j])&&dp[i+1][j-1]==j-i-1&&dp[j+1][k-1]==k-j-1){
// printf("part3 ask (%d,%d) (%d,%d)\n",l,i-1,k+1,r);
dp[l][r]=max(dp[l][r],(k-i+1)+dp[l][i-1]+dp[k+1][r]);
}
}
}
}
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int i,j,k;
for(i=1;i<=n;++i) scanf("%d",a+i);
for(i=1;i<=m;++i) scanf("%d",d+i);
memset(f,0,sizeof(f));
for(i=1;i<n;++i)
for(j=i+1;j<=n;++j)
for(k=1;k<=m;++k) f[i][j]|=(a[j]-a[i]==d[k]);
solve();
printf("%d\n",dp[1][n]);
}
return 0;
}

我们发现了一个枚举的方法是

在区间[l,r],要么我们只取l,r这两个数删掉

要么枚举在区间[l,r]内的分割点k,于是我们只需要考虑l,k,r这三个数能不能删掉

注意到我们l,r是必选的。。这样就不能形成最后一次删掉的数字在中间

于是我们枚举l,r不是必选的情况,递归分成两个子区间,将这个不选的决策交给子区间,这样我们就发现有了这个分解的步骤

即使采用了上述前两个策略。。凭借只用短长度区间l,r全选和,l,k,r全选就能形成所有的决策,我认为这个想法是非常巧妙的

虽然大佬们认为可能这很显然Orz,但是不得不说这种递归策略非常巧妙。。可能是我还没掌握精髓吧。。

放上1499ms/3000ms的代码

细节:小心r越界,因为我的len一直枚举到n,

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring> using namespace std;
int T,n,m;
int a[305],d[305];
bool f[305][305];
//定向从左往右删除
int dp[305][305];
void solve(){
memset(dp,0,sizeof(dp));
int l,r,i,j,k,len;
for(len=2;len<=n;++len){
for(l=1;l<n;++l){
r=l+len-1;
// printf("DP (%d,%d)\n",l,r);
if(r>n) continue;
if(l>=r) continue;
// printf("ASK (%d,%d) \n",l+1,r-1);
if(f[l][r]&&dp[l+1][r-1]==r-l-1)
dp[l][r]=max(dp[l][r],2+dp[l+1][r-1]);
for(i=l;i<r;++i) {
// printf("ASK (%d,%d) (%d,%d)\n",l,i,i+1,r);
dp[l][r]=max(dp[l][r],dp[l][i]+dp[i+1][r]);//当前区间保留头尾的情况
//这一句是我所需要的精华。。
}
for(k=l;k<=r;++k){
// printf("ASK (%d,%d) (%d,%d)\n",l+1,k-1,k+1,r-1);
if(f[l][k]&&f[k][r]&&(a[k]-a[l]==a[r]-a[k])&&dp[l+1][k-1]==k-l-1&&dp[k+1][r-1]==r-k-1){
dp[l][r]=max(dp[l][r],r-l+1);
}
}
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int i,j,k;
for(i=1;i<=n;++i) scanf("%d",a+i);
for(i=1;i<=m;++i) scanf("%d",d+i);
memset(f,0,sizeof(f));
for(i=1;i<n;++i)
for(j=i+1;j<=n;++j)
for(k=1;k<=m;++k) f[i][j]|=(a[j]-a[i]==d[k]);
solve();
printf("%d\n",dp[1][n]);
}
return 0;
}

hdu5693D++游戏 区间DP-暴力递归的更多相关文章

  1. 圆桌游戏(区间DP)

    2.圆桌游戏 (game.cpp/c/pas) [问题描述] 有一种圆桌游戏是这样进行的:n个人围着圆桌坐成一圈,按顺时针顺序依次标号为1号至n号.对1<=i<=n的i来说,i号的左边是i ...

  2. P1005 矩阵取数游戏[区间dp]

    题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的\(m*n\)的矩阵,矩阵中的每个元素\(a_{i,j}\)均为非负整数.游戏规则如下: 每次取数时须从每行各取走一个元素,共n个.经过m次后 ...

  3. BZOJ 2121: 字符串游戏 区间DP + 思维

    Description BX正在进行一个字符串游戏,他手上有一个字符串L,以及其他一些字符串的集合S,然后他可以进行以下操作:对 于一个在集合S中的字符串p,如果p在L中出现,BX就可以选择是否将其删 ...

  4. 洛谷 P1043 数字游戏 区间DP

    题目描述 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易.游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分 ...

  5. 多边形游戏——区间dp

    题目描述 多边形(Polygon)游戏是单人玩的游戏,开始的时候给定一个由N个顶点构成的多边形(图1所示的例子中,N=4),每个顶点被赋予一个整数值,而每条边则被赋予一个符号:+(加法运算)或者*(乘 ...

  6. qscoj 喵哈哈村的打印机游戏 区间dp

    点这里去看题 区间dp ,dp[l][r][d]代表从l到r的区间底色为d,具体看代码 第一次见到区间dp...两个小时对着敲了五遍终于自己敲懂了一遍ac #include<bits/stdc+ ...

  7. 【bzoj2121】字符串游戏 区间dp

    题目描述 给你一个字符串L和一个字符串集合S,如果S的某个子串在S集合中,那么可以将其删去,剩余的部分拼到一起成为新的L串.问:最后剩下的串长度的最小值. 输入 输入的第一行包含一个字符串,表示L. ...

  8. Leetcode_877. 石子游戏(区间dp)

    偶数堆石子,只能从首尾取,取多的赢. 每次操作会产生两个子状态,区间dp,记得先枚举长度. code class Solution { public: int dp[505][505]; bool s ...

  9. 1166 矩阵取数游戏[区间dp+高精度]

    1166 矩阵取数游戏 2007年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description [ ...

随机推荐

  1. 《进击吧!Blazor!》第一章 1.初识 Blazor

    作者介绍 陈超超 Ant Design Blazor 项目贡献者 拥有十多年从业经验,长期基于.Net技术栈进行架构与开发产品的工作,Ant Design Blazor 项目贡献者,现就职于正泰集团 ...

  2. PAT练习num4-D进制的A+B

    输入两个非负 10 进制整数 A 和 B (≤),输出 A+B 的 D (1)进制数. 输入格式: 输入在一行中依次给出 3 个整数 A.B 和 D. 输出格式: 输出 A+B 的 D 进制数. 输入 ...

  3. python工业互联网应用实战3—Django Admin列表

    Django Admin笔者使用下来可以说是Django框架的开发利器,业务model构建完成后,我们就能快速的构建一个增删查改的后台管理框架.对于大量的企业管理业务开发来说,可以快速的构建一个可发布 ...

  4. (17)-Python3之--文件操作

    1.文件的操作流程 第一,建立文件对象. 第二,调用文件方法进行操作. 第三,不要忘了关闭文件.(文件不关闭的情况下,内容会放在缓存,虽然Python会在最后自动把内容读到磁盘,但为了以防万一,要养成 ...

  5. (10)-Python3之--引入

    1.什么是模块 .py文件就是模块 模块名有命名要求: 1.不要以数字.下划线开头.特殊符号.也不要以中文开头. 2.通常来说,都是以字母开头. 3.不要以关键字来命名.内置函数.内置模块.不要以第三 ...

  6. git的使用学习笔记4--创建分支

    1.在git上新建分支 查看本地分支 git branch 查看远程分支 git branch -a 创建一个分支 git checkout -b branch1 再次查看远程分支可以看到该分支 2. ...

  7. lodash的debounce函数的使用

    最新,在react新项目的开发中使用到了lodash类库的debounce方法,就随手梳理了一下此方法的方便之处 未使用debounce之前 如果不考虑使用debounce,那么在用户连续点击的情况之 ...

  8. (五)整合 Swagger2 ,构建接口管理界面

    整合 Swagger2 ,构建接口管理界面 1.Swagger2简介 1.1 Swagger2优点 1.2 Swagger2常用注解 2.SpringBoot整合Swagger2 2.1 Swagee ...

  9. MySql(三)存储过程和函数

    MySql(三)存储过程和函数 一.什么是存储过程和函数 二.存储过程和函数的相关操作 一.什么是存储过程和函数 存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程和函数 ...

  10. 通过jenkins构建服务,并发布服务,修改Jenkins以Root用户运行

    通过jenkins构建服务,并发布服务,修改Jenkins以Root用户运行 其他博文:从0到1体验Jenkins+Docker+Git+Registry实现CI自动化发布 Jenkins注册中心 一 ...