P1004 方格取数——奇怪的dp
P1004 方格取数
题目描述
设有 \(N\times N\) 的方格图 \((N\leq 20)\),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 \(0\) 。如下图所示(见样例):
A
0 0 0 0 0 0 0 0
0 0 13 0 0 6 0 0
0 0 0 0 7 0 0 0
0 0 0 14 0 0 0 0
0 21 0 0 0 4 0 0
0 0 15 0 0 0 0 0
0 14 0 0 0 0 0 0
0 0 0 0 0 0 0 0
B
某人从图的左上角的 \(A\) 点出发,可以向下行走,也可以向右走,直到到达右下角的 \(B\) 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 \(0\) )。
此人从 \(A\) 点到 \(B\) 点共走两次,试找出 \(2\) 条这样的路径,使得取得的数之和为最大。
输入格式
输入的第一行为一个整数 \(N\) (表示 \(N\times N\) 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 \(0\) 表示输入结束。
输出格式
只需输出一个整数,表示 \(2\) 条路径上取得的最大的和。
输入输出样例
输入
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
输出
67
说明/提示
NOIP 2000 提高组第四题
错误思路
刚开始看题,肯定会想出暴力 \(Dfs\) 进行第一遍,把走过的置为 \(0\) ,然后再 \(Dfs\) 第二遍,将两次结果加和就是答案。(我就是这么错的)
为什么错呢?
首先,记录路径很麻烦,其次有些数据可以正好把 \(Dfs\) 卡掉(如下图),最后是效率太差了。
A
0 0 2 3 0 0 0
0 0 3 0 0 0 0
0 0 3 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 4 0 0
0 0 0 0 4 0 0
0 0 2 0 4 0 0
B
很明显正确走法是
\((1,1)—>(1,2)—>(1,3)—>(1,4)—>(1,5)—>···—>(5,5)—>(6,5)—>(7,5)—>(7,6)—>(7,7)\)
第二次便可把剩下的都走完。
但是走 \(Dfs\) ,第一次走的是值最大的路径
\((1,1)—>(1,2)—>(1,3)—>(2,3)—>(3,3)—>(4,3)—>(5,3)—>(5,4)—>(5,5)—>(6,5)—>(7,5)—>(7,6)—>(7,7)\)
第二次剩下的 \(2\) 和 \(3\) 只能走一个,就被卡掉了。
暴力错码(Dfs)
#include <bits/stdc++.h>
using namespace std;
int pan[25][25];
int pathx;
int pathy;
int n;
int maxtot;
int ans1;
int ans2;
int ans[25][25];
void dfs1(int x,int y,int cnt,int tot){//走第一遍
if(x<=0||x>n||y<=0||y>n){
return;
}
if(x==n&&y==n){
ans1=max(ans1,cnt);
if(ans1==cnt){
maxtot=max(maxtot,tot);
}
return;
}
cnt+=pan[x][y];
if(pan[x][y]!=0){
tot++;
}
ans[x][y]=max(ans[x][y],cnt);
dfs1(x+1,y,cnt,tot);
dfs1(x,y+1,cnt,tot);
}
void dfspath(int x,int y,int cnt,int tot){//路径的记录,效率太差了
if(x<=0||x>n||y<=0||y>n){
return;
}
if(x==n&&y==n){
return;
}
cnt+=pan[x][y];
if(pan[x][y]!=0){
tot++;
if(ans1==cnt){
pathx=x;
pathy=y;
}
}
dfspath(x+1,y,cnt,tot);
dfspath(x,y+1,cnt,tot);
}
int xsx;
void Find(int x,int y){
if(x<=0||x>n||y<=0||y>n||xsx==0){
return;
}
if(x==1&&y==1){
xsx-=pan[x][y];
pan[x][y]=0;
return;
}
for(int i=1;i<=x;i++){
for(int j=1;j<=y;j++){
if(ans[i][j]+pan[x][y]==xsx){
xsx-=pan[x][y];
pan[x][y]=0;
Find(i,j);
break;
}
}
}
}
void dfs2(int x,int y,int cnt,int tot){//走第二遍
if(x<=0||x>n||y<=0||y>n){
return;
}
if(x==n&&y==n){
ans2=max(ans2,cnt);
return;
}
cnt+=pan[x][y];
dfs2(x+1,y,cnt,tot);
dfs2(x,y+1,cnt,tot);
}
int main(){
scanf("%d",&n);
int x,y,w;
while(scanf("%d%d%d",&x,&y,&w)==3){
if(x==0&&y==0&&w==0)break;
pan[x][y]=w;
}
dfs1(1,1,0,0);
dfspath(1,1,0,0);
ans[n][n]=ans1;
xsx=ans1;
Find(pathx,pathy);
dfs2(1,1,0,0);
cout<<ans1+ans2<<endl;
return 0;
}
正解思路
首先,我们可以把一个人走两遍,看成两个人走一遍,但是当两人走到同一个有分值的地方时,只能加一次改点的值。
所以可以用 \(dp\) 来解决, \(dp[i][j][l][r]\) 表示当第一个人在 \((i,j)\) ,第二个人在 \((l,r)\) 时所取得的最大值。
因为 \(n\) 比较小,撑死了到 \(20\) ,所 \(dp\) 的效率和四维的数组还是可以接受的,毕竟 \(Dfs\) 过 \(4\) 个点就走了 \(4000ms\) 。
所以将 \(i\)、\(j\)、\(l\)、\(r\) 依次从 \(1\) 到 \(n\) 枚举,取最大值即可,注意判断两个人在同一位置的情况。
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int pan[25][25];//记录每个点的值
int dp[25][25][25][25];
int main(){
scanf("%d",&n);
int x,y,w;
while(scanf("%d%d%d",&x,&y,&w)==3){
if(x==0&&y==0&&w==0)break;
pan[x][y]=w;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int l=1;l<=n;l++){
for(int r=1;r<=n;r++){
if(i==l&&j==r){//若两人在同一位置只用加一次pan[i][j]的值
dp[i][j][l][r]=pan[i][j]+max(max(dp[i-1][j][l-1][r],dp[i-1][j][l][r-1]),max(dp[i][j-1][l-1][r],dp[i][j-1][l][r-1]));
}else{//两人不在同一点,两个值都加上即可
dp[i][j][l][r]=pan[i][j]+pan[l][r]+max(max(dp[i-1][j][l-1][r],dp[i-1][j][l][r-1]),max(dp[i][j-1][l-1][r],dp[i][j-1][l][r-1]));
}
}
}
}
}
cout<<dp[n][n][n][n]<<endl;
return 0;
}
优化
还可以把的 \(for\) 循环优化成三维的,利用小人只能向下和向右走的性质,他的横坐标加上纵坐标一定等于时间。
\(dp\) 数组意义同上,利用第一个小人的横纵坐标,减去第二个小人的横坐标,便可把第四层循环省去。也就省去了 \(10ms\) 吧
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int pan[25][25];
int dp[25][25][25][25];
int main(){
scanf("%d",&n);
int x,y,w;
while(scanf("%d%d%d",&x,&y,&w)==3){
if(x==0&&y==0&&w==0)break;
pan[x][y]=w;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int l=1;l<=n;l++){
int r=i+j-l;//利用i、j求出第二个小人的纵坐标
if(i==l&&j==r){
dp[i][j][l][r]=pan[i][j]+max(max(dp[i-1][j][l-1][r],dp[i-1][j][l][r-1]),max(dp[i][j-1][l-1][r],dp[i][j-1][l][r-1]));
}else{
dp[i][j][l][r]=pan[i][j]+pan[l][r]+max(max(dp[i-1][j][l-1][r],dp[i-1][j][l][r-1]),max(dp[i][j-1][l-1][r],dp[i][j-1][l][r-1]));
}
}
}
}
cout<<dp[n][n][n][n]<<endl;
return 0;
}
P1004 方格取数——奇怪的dp的更多相关文章
- P1004 方格取数(四维dp)
P1004 方格取数 思路如下 这题是看洛谷大佬的思路才写出来的,所以我会把大佬的思路展示如下: 1⃣️:我们可以找到一个叫思维dp的东西,dp[i][j][k][l],其中前两维表示一个人从原点出发 ...
- 洛谷 P1004 方格取数 【多进程dp】
题目链接:https://www.luogu.org/problemnew/show/P1004 题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 ...
- 洛谷 P1004 方格取数 【多线程DP/四维DP/】
题目描述(https://www.luogu.org/problemnew/show/1004) 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0. ...
- 棋盘DP三连——洛谷 P1004 方格取数 &&洛谷 P1006 传纸条 &&Codevs 2853 方格游戏
P1004 方格取数 题目描述 设有N $\times N$N×N的方格图(N $\le 9$)(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字00.如下图所示(见样例): A ...
- [动态规划]P1004 方格取数
---恢复内容开始--- 题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 ...
- 洛谷 P1004 方格取数 题解
P1004 方格取数 题目描述 设有 \(N \times N\) 的方格图 \((N \le 9)\),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字\(0\).如下图所示(见样例): ...
- HDU 1565 方格取数(1) 轮廓线dp
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1565 方格取数(1) Time Limit: 10000/5000 MS (Java/Others) ...
- tyvj 1884 [NOIP2000T4]方格取数 || codevs 1043 dp
P1884 [NOIP2000T4]方格取数 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 [noip2000T4]方格取数 描述 设有N*N的方格图(N& ...
- codevs_1043 方格取数(棋盘DP)
1043 方格取数 2000年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description ...
随机推荐
- 自己动手写SQL执行引擎
自己动手写SQL执行引擎 前言 在阅读了大量关于数据库的资料后,笔者情不自禁产生了一个造数据库轮子的想法.来验证一下自己对于数据库底层原理的掌握是否牢靠.在笔者的github中给这个database起 ...
- Spring之JdbcTemplate使用
一:JdbcTemplate概述及入门 “Don‘t Reinvent the Wheel” , 这是一句很经典的话,出自Spring官方,翻译过来就是说 “不要重复发明轮子” .由此我们可以猜测,J ...
- Git Gui工具从远程克隆代码总是提示路径已经存在。问题完美解决!
最近使用Git Gui工具从远程克隆代码总是提示路径已经存在,提示如下图, 原因是使用Git Guii克隆代码时的路径不能是选择一个已经建好的目录(D盘的project下的test文件夹是我提前建好的 ...
- 【大厂面试04期】讲讲一条MySQL更新语句是怎么执行的?
流程图 这是在网上找到的一张流程图,写的比较好,大家可以先看图,然后看详细阅读下面的各个步骤. 执行流程: 1.连接验证及解析 客户端与MySQL Server建立连接,发送语句给MySQL Serv ...
- php symfony/var-dumper 打印插件
$records = array( array( 'id' => 2135, 'first_name' => 'John', 'last_name' => 'Doe', ), arr ...
- OAuth + Security - 5 - Token存储升级(数据库、Redis)
PS:此文章为系列文章,建议从第一篇开始阅读. 在我们之前的文章中,我们当时获取到Token令牌时,此时的令牌时存储在内存中的,这样显然不利于我们程序的扩展,所以为了解决这个问题,官方给我们还提供了其 ...
- HTML 5的革新——语义化标签section和article的区别
原文地址:https://stackoverflow.com/questions/33910294/what-is-the-difference-between-article-and-section ...
- 【 转】百度地图Canvas实现十万CAD数据秒级加载
Github上看到: https://github.com/lcosmos/map-canvas 这个实现台风轨迹,这个数据量非常庞大,当时打开时,看到这么多数据加载很快,感到有点震惊,然后自己研究了 ...
- spring cloud 集成分布式配置中心 apollo(单机部署apollo)
一.什么是apollo? Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用 ...
- nacos基础--客户端下载
对于nacos的作用,我在这里不在过多介绍,不知道的同学可以自行先了解,对于nacos,有官网进行介绍,对于一个初学者来说是一件非常方便的事情. 官网地址:https://nacos.io 但是在下载 ...