一、方格取数:

设有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增加了!的更多相关文章

  1. 小烈送菜——奇怪的dp

    小烈送菜 题目描述 小烈一下碰碰车就被乐满地的工作人员抓住了.作为扰乱秩序的惩罚,小烈必须去乐满地里的"漓江村"饭店端盘子. 服务员的工作很繁忙.他们要上菜,同时要使顾客们尽量高兴 ...

  2. 线性DP之小烈送菜

    小烈送菜 小烈一下碰碰车就被乐满地的工作人员抓住了.作为扰乱秩序的惩罚,小烈必须去乐满地里的"漓江村"饭店端盘子. 服务员的工作很繁忙.他们要上菜,同时要使顾客们尽量高兴.一位服务 ...

  3. [HNOI2009]双递增序列(洛谷P4728)+小烈送菜(内部训练题)——奇妙的dp

    博主学习本题的经过嘤嘤嘤: 7.22 : 听学长讲(一知半解)--自己推(推不出来)--网上看题解--以为自己会了(网上题解是错的)--发现错误以后又自己推(没推出来)--给学长发邮件--得到正确解法 ...

  4. 洛谷 - P1004 - 方格取数 - 简单dp

    https://www.luogu.org/problemnew/show/P1004 这道题分类到简单dp但是感觉一点都不简单……这种做两次的dp真的不是很懂怎么写.假如是贪心做两次,感觉又不能证明 ...

  5. HDU 1565 1569 方格取数(最大点权独立集)

    HDU 1565 1569 方格取数(最大点权独立集) 题目链接 题意:中文题 思路:最大点权独立集 = 总权值 - 最小割 = 总权值 - 最大流 那么原图周围不能连边,那么就能够分成黑白棋盘.源点 ...

  6. HDU 1565&1569 方格取数系列(状压DP或者最大流)

    方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  7. hdu 1565 方格取数(1) 状态压缩dp

    方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  8. 棋盘DP三连——洛谷 P1004 方格取数 &&洛谷 P1006 传纸条 &&Codevs 2853 方格游戏

    P1004 方格取数 题目描述 设有N $\times N$N×N的方格图(N $\le 9$)(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字00.如下图所示(见样例): A ...

  9. 洛谷 P1004 方格取数 题解

    P1004 方格取数 题目描述 设有 \(N \times N\) 的方格图 \((N \le 9)\),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字\(0\).如下图所示(见样例): ...

随机推荐

  1. Android 4.X 系统加载 so 失败的原因分析

    1 so 加载过程 so 加载的过程可以参考小米的系统工程师的文章loadLibrary动态库加载过程分析 2 问题分析 2.1 问题 年前项目里新加了一个 so库,但发现native 方法的找不到的 ...

  2. Linux系统环境基于Docker搭建Mysql数据库服务实战

    开放端口规划: mysql-develop:3407 mysql-test: 3408 mysql-release: 3409 ps: 1.不推荐使用默认端口-3306,建议自定义端口 2.如果采用阿 ...

  3. Fliptile(POJ 3279)

    原题如下: Fliptile Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16494   Accepted: 6025 D ...

  4. selenuim常用api

    1.通过代码跳转到页面的方法:前提:已安装驱动,并加入到path下 安装ie驱动或火狐驱动的情况: 2.当链接跳转按钮在页面最上方时,用window.scrollTo(0,0);显示在页面最上方后,再 ...

  5. 一条 SQL 引发的事故,同事直接被开除!!

    前言 Insert into select请慎用. 这天xxx接到一个需求,需要将表A的数据迁移到表B中去做一个备份.本想通过程序先查询查出来然后批量插入.但xxx觉得这样有点慢,需要耗费大量的网络I ...

  6. Centos6.5 离线 Openssh 升级

    目录 OpenSSH 升级 一.基于 Dropbear 设置备用 ssh 服务器 二. Openssh 更新 2.1 ssh配置 备份 2.2 openssh 升级 Openssl 升级(由于Open ...

  7. Spark Pipeline官方文档

    ML Pipelines(译文) 官方文档链接:https://spark.apache.org/docs/latest/ml-pipeline.html 概述 在这一部分,我们将要介绍ML Pipe ...

  8. 永远不要在代码中使用“User”这个单词

    ​ 当你意识到你在项目开始时做的轻量.简单的设想竟然完全错了时,你已经用了六个月的时间投入到这个项目上.现在你需要解决这些问题,才能让这个系统继续运行下去,你发现你用在这个项目上的精力远远超出了你的预 ...

  9. 编写一个Open Live Writer的VSCode代码插件

    起因 又是一年多没有更新过博客了,最近用Arduino做了一点有意思的东西,准备写一篇博客.打开尘封许久的博客园,发现因为Windows Live Writer停止更新,博客园推荐的客户端变为了Ope ...

  10. Power Designer建模之餐饮在线点评系统——业务处理模型

    餐饮在线点评系统除查看会员促销活动.查看站内消息等简单业务流程外,相对复杂的业务流程包括管理员注册餐厅,发布餐厅信息,餐厅信息主要包括特色菜.促销活动.团购活动和优惠券信息. 餐厅信息发布后,用户可以 ...