方格取数(简单版)+小烈送菜(不知道哪来的题)-----------奇怪的dp增加了!
一、方格取数:
设有N*N的方格图(N<=20),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
某人从图的左上角的A(1,1) 点出发,可以向下行走,也可以向右走,直到到达右下角的B(n,n)点。在走过的路上(包括起点在内),他可以取走方格中的数(取走后的方格中将变为数字0)。此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
思路:emm...这咋写啊,好像是dp???可是你选了一个数后,第二次取数的时候不就不能取了,这不是有后效性了吗。。。
所以这道题的思路很清奇:两个人同时走。
这啥意思呢,简单来说,你的dp数组需要开四维,同时记录两个人的位置及状态,dp[i1][j1][i2][j2]。
既然我们一个人走两次会有后效性,那么我们可以认为是两个人同时在走,同时取数,这样就没有后效性了。
同时,转移的时候,就会有四种转移过程,第一个人可以从左或是从上过来,第二个人亦是。
转移方程:if(i1==i2&&j1==j2)dp[i1][j1][i2][j2]=max(dp[i1][j1][i2][j2],四种情况)+val[i1][j1];(这个意思是指两个人恰好走到一起去了,但是数只能取一次)
else dp[i1][j1][i2][j2]=max(dp[i1][j1][i2][j2],四种情况)+val[i1][j1]+val[i2][j2];
如下:
if(i1==i2&&j1==j2)dp[i1][j1][i2][j2]=max(dp[i1][j1-1][i2][j2-1],max(dp[i1-1][j1][i2][j2-1],max(dp[i1-1][j1][i2-1][j2],dp[i1][j1-1][i2-1][j2])))+val[i1][j1];
else dp[i1][j1][i2][j2]=max(dp[i1][j1-1][i2][j2-1],max(dp[i1-1][j1][i2][j2-1],max(dp[i1-1][j1][i2-1][j2],dp[i1][j1-1][i2-1][j2])))+val[i1][j1]+val[i2][j2];
啊啊啊啊太丑了太丑了
还有优化的方法:我们注意到,i1+j1==i2+j2==time(因为同时走的,速度也一样)
我们可以压缩成三维dp[i1][i2][time]遍历的时候跟上面大体一样,只要用time-i代替j就行了,注意time的循环范围!
代码全貌:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=21;
int dp[21][21][21][21],val[21][21];
int main(){
int n;scanf("%d",&n);
while(1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(x==0&&y==0&&z==0)break;
val[x][y]=z;
}
for(int i1=1;i1<=n;i1++){
for(int j1=1;j1<=n;j1++){
for(int i2=1;i2<=n;i2++){
for(int j2=1;j2<=n;j2++){
if(i1==i2&&j1==j2){
dp[i1][j1][i2][j2]=max(dp[i1][j1-1][i2][j2-1],max(dp[i1-1][j1][i2][j2-1],max(dp[i1-1][j1][i2-1][j2],dp[i1][j1-1][i2-1][j2])))+val[i1][j1];
}else{
dp[i1][j1][i2][j2]=max(dp[i1][j1-1][i2][j2-1],max(dp[i1-1][j1][i2][j2-1],max(dp[i1-1][j1][i2-1][j2],dp[i1][j1-1][i2-1][j2])))+val[i1][j1]+val[i2][j2];
}
}
}
}
}
printf("%d",dp[n][n][n][n]);
return 0;
}
二、小烈送菜
题目大意:一共有n位客人从1到n顺序排列,每位顾客i有满意值W[i],服务员小烈可以从1到n再从n走到1,在期间他到达i时可以选择送菜或不送,但是要求最后回到1时所有菜都送完,当送完菜给i后再送菜给j时,可以得到满意值Wi×Wj,问如何上菜总满意值最大(第一位被送菜的顾客不提供满意值)
思路分析:
这道题与方格取数很类似,小烈需要从1到n再从n到1走两遍,所以我们可以考虑同时维护两个小烈同时走,然后到n结束。
性质1:W[i]*W[j]==W[j]*W[i]
这意味着你从1到n走一遍和从n到1走一遍是等效的,你完全可以认为你从1到n中送菜的顾客1--->i--->j--->n满意度是W[1]*W[i]+W[i]*W[j]+W[j]*W[n],如果你反过来也一样:n--->j--->i--->1满意度是W[n]*W[j]+W[j]*W[i]+W[i]*W[1];显然两者等效。
(通过这条性质,我们可以把思路中的两个小烈都改为从1到n去走)
接下来我们需要定义dp数组:
因为需要定义的无后效性,所以我们保证:当小烈1位于i位置送菜,小烈2位于j位置送菜时,所有位置小于i、j的顾客都已经被送过菜了。
dp[i][j]表示小烈1位于i,小烈2位于j时的满意度最大值。
转移方程:
i可以从前一个状态k转移过来,j也是,但是这样去枚举k1,k2很麻烦(基本算不出来),所以我们考虑由i,j向后推。
我们可以知道,因为dp[i][j]表示i,j之前的所有点都被遍历过,所以i,j的下一个状态一定是i,i+1或i+1,j或j+1,j或i,j+1。这个根据i,j的大小而定,dp[i][j]的下一个状态一定是i,j两个小烈中靠前的那个的前面一步(否则就不满足i,j前的所有顾客都被遍历过了)。我们可以得到这样的方程:
if(i>j){
dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]);
dp[i][i+1]=max(dp[i][i+1],dp[i][j]+a[j]*a[i+1]);
}else{
dp[i][j+1]=max(dp[i][j+1],dp[i][j]+a[j]*a[j+1]);
dp[j+1][j]=max(dp[j+1][j],dp[i][j]+a[i]*a[j+1]);
}
这样写固然是没有问题啦,但是4行代码还是有点复杂,我们可以这样想:
如果保证dp[i][j]中i,j的大小关系,那么就可以把转移方程缩至两个。
我们不妨保证i>j。(在遍历的时候j从0到i-1即可)
那么方程变成:
dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]);
dp[i][i+1]=max(dp[i][i+1],dp[i][j]+a[j]*a[i+1]);
这样已经很优秀了,可是我们看到第两个方程中i+1>i,这并不满足我们的前提条件i>j
这时候我们要用到性质2:
dp[i][j]=dp[j][i]这很显然吧,两个小烈本质上是一样的,谁在i,谁在j不重要,结果也一定相同。
所以我们可以改造一下dp[i][i+1],把它变成dp[i+1][i],这样就满足我们的i>j了。
最后的完成方程:
dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]);
dp[i+1][i]=max(dp[i+1][i],dp[i][j]+a[j]*a[i+1]);
代码全貌:
#include<bits/stdc++.h>
using namespace std;
const int maxn=5510;
int dp[maxn][maxn],a[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){//注意这里j从0开始遍历,因为存在一种可能:一个小烈送完了1到i的所有客人菜
dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]);
dp[i+1][i]=max(dp[i+1][i],dp[i][j]+a[j]*a[i+1]);
}
}
int Max=0;
for(int i=0;i<n;i++){
Max=max(Max,dp[n][i]+a[i]*a[n]);
}
//我们假设第一个小烈已经到了终点,那么第二个小烈只需要从当前位置到n即可,因为i到n的所有点已经被第一个小烈走过了
//而实际上n这个点是要“被送两次的”,因为小烈到n再回去的时候a[n]会用两次。
printf("%d",Max);
return 0;
}
方格取数(简单版)+小烈送菜(不知道哪来的题)-----------奇怪的dp增加了!的更多相关文章
- 小烈送菜——奇怪的dp
小烈送菜 题目描述 小烈一下碰碰车就被乐满地的工作人员抓住了.作为扰乱秩序的惩罚,小烈必须去乐满地里的"漓江村"饭店端盘子. 服务员的工作很繁忙.他们要上菜,同时要使顾客们尽量高兴 ...
- 线性DP之小烈送菜
小烈送菜 小烈一下碰碰车就被乐满地的工作人员抓住了.作为扰乱秩序的惩罚,小烈必须去乐满地里的"漓江村"饭店端盘子. 服务员的工作很繁忙.他们要上菜,同时要使顾客们尽量高兴.一位服务 ...
- [HNOI2009]双递增序列(洛谷P4728)+小烈送菜(内部训练题)——奇妙的dp
博主学习本题的经过嘤嘤嘤: 7.22 : 听学长讲(一知半解)--自己推(推不出来)--网上看题解--以为自己会了(网上题解是错的)--发现错误以后又自己推(没推出来)--给学长发邮件--得到正确解法 ...
- 洛谷 - P1004 - 方格取数 - 简单dp
https://www.luogu.org/problemnew/show/P1004 这道题分类到简单dp但是感觉一点都不简单……这种做两次的dp真的不是很懂怎么写.假如是贪心做两次,感觉又不能证明 ...
- HDU 1565 1569 方格取数(最大点权独立集)
HDU 1565 1569 方格取数(最大点权独立集) 题目链接 题意:中文题 思路:最大点权独立集 = 总权值 - 最小割 = 总权值 - 最大流 那么原图周围不能连边,那么就能够分成黑白棋盘.源点 ...
- HDU 1565&1569 方格取数系列(状压DP或者最大流)
方格取数(2) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
- hdu 1565 方格取数(1) 状态压缩dp
方格取数(1) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- 棋盘DP三连——洛谷 P1004 方格取数 &&洛谷 P1006 传纸条 &&Codevs 2853 方格游戏
P1004 方格取数 题目描述 设有N $\times N$N×N的方格图(N $\le 9$)(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字00.如下图所示(见样例): A ...
- 洛谷 P1004 方格取数 题解
P1004 方格取数 题目描述 设有 \(N \times N\) 的方格图 \((N \le 9)\),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字\(0\).如下图所示(见样例): ...
随机推荐
- 接口自动化---简单的数据驱动框架ATP(基于excel)
数据驱动测试:根据数据进行测试.将用例写入excel文件,用代码读取文件中的数据,从而实现自动化测试. 自动化框架实现步骤: 1.获取用例 2.调用接口 3.校验结果 4.发送测试报告 5.异常处理 ...
- python基础四(json\os\sys\random\string模块、文件、函数)
一.文件的修改 文件修改的两种思路: 1.把文件内容拿出来,做修改后,清空原来文件的内容,然后把修改过的文件内容重新写进去. 步骤: 1.打开文件:f=open('file','a+') #必须用a ...
- springboot2.x基础教程:动手制作一个starter包
上一篇博客介绍了springboot自动装配的原理.springboot本身有丰富的spring-boot-starter-xx集成组件,这一篇趁热打铁加深理解,我们利用springboot自动装配的 ...
- bind,call,apply模拟实现
首先,三者第一个参数都为this指向 区别 bind返回的是一个函数体 call和apply会直接执行,但是call参数需要一个一个进行传递,apply的第二个参数是一个数组 实现 bind 简单实现 ...
- openstack核心组件——horizon Web管理界面(10)
一.horizon 介绍: 理解 horizon Horizon 为 Openstack 提供一个 WEB 前端的管理界面 (UI 服务 )通过 Horizone 所提供的 DashBoard 服务 ...
- Java基础语法(10)-面向对象之三大特征
title: Java基础语法(9)-面向对象之类的成员 blog: CSDN data: Java学习路线及视频 1.面向对象特征--封装 为什么需要封装?封装的作用和含义? 我要用洗衣机,只需要按 ...
- 关于windows服务器的Security安全类日志的导出
对于windows服务器,日志的分类会有很多,可以通过wevtutil el 列出 有时可能会对Security安全类日志进行审计,这里简单讲一下安全类日志的导出方法 1.直接在cmd中执行 wevt ...
- 面经手册 · 第12篇《面试官,ThreadLocal 你要这么问,我就挂了!》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 说到底,你真的会造火箭吗? 常说面试造火箭,入职拧螺丝.但你真的有造火箭的本事吗,大 ...
- 逻辑漏洞介绍 & 越权访问攻击 & 修复建议
介绍逻辑漏洞 逻辑漏洞就是指攻击者利用业务的设计缺陷,获取敏感信息或破坏业务的完整性.一般出现在密码修改.越权访问.密码找回.交易支付金额等功能处.其中越权访问又有水平越权和垂直越权两种,如下所示. ...
- dbdeployer MySQL沙盒部署详解
一.工具介绍 前几日用mysql-sandbox来搭建MySQL8.0新版本时发现用不了,提示需要使用dbdeployer才行,瞬间觉得mysql-sandbox不香了,只好咬咬牙来熟悉dbdeplo ...