我的DP训练计划:三取方格数

题面:

背景

JerryZhou同学经常改编习题给自己做。

这天,他又改编了一题。。。。。

描述

设有N*N的方格图,我们将其中的某些方格填入正整数,
而其他的方格中放入0。

某人从图得左上角出发,可以向下走,也可以向右走,直到到达右下角。

在走过的路上,他取走了方格中的数。(取走后方格中数字变为0)
此人从左上角到右下角共走3次,试找出3条路径,使得取得的数总和最大。

格式

输入格式

第一行:N (4<=N<=20)
接下来一个N*N的矩阵,矩阵中每个元素不超过80,不小于0

输出格式

一行,表示最大的总和。

听...听说是一类经典DP...

反正我是不会...

一开始我的思路就很一根筋:直接按走一次做,然后重复三遍

然后,然后就听取WA声一片...

反正最后也没想出来,是看的题解的思路...呜呜呜我太菜了

有一个比较重要的结论是:

  在矩阵中,如果只能向下走或者向右走,那么,知道起点的前提下,步数,行数,列数,知道其中两个能推第三个

  并且,走到同一位置的两个点,必定花费了相同的步数

这些结论支撑起了我们的思路:让三个点同时走

下面试着构建状态:f[x][i][j][k],代表走了x步,i,j,k代表三个人分别走到了第i行,第j行,第k行。

其实也就等价于走到了第i行第x-i+2列,第j行第x-j+2列,第k行第x-k+2列。

对于每一个状态,这个状态之前最多会有8种状态(每个点分别从左边和从上边过来),也就是说,要从这8个状态中选一个最好的状态来更新当前状态。

写成方程就是:

f[x,i,j,k]=max(f[x-1,i-1,j,k],f[x-1,i,j-1,k],f[x-1,i,j,k-1], //其中一个从上边来          f[x-1,i-1,j-1,k],f[x-1,i-1,j,k-1],f[x-1,i,j-1,k-1], //其中两个从上边来          f[x-1,i-1,j-1,k-1], //三个都从上边来          f[x-1,i,j,k]) //三个都不从上边来          +add //对应需要加上的 一个/两个/三个 点的值

(代码里没特判边界是因为无效的前一个状态对应的f是0,也就是说,如果有任何一个合法的上一个状态都要比它大)

由于格子里的数被取走就没了,所以要判断有没有任意两/三个点走到了同一个格子,不要加多了。(我就在这里犯傻了...三个位置两两不同是:i!=j&&j!=k&&i!=k,我当成了i!=j&&j!=k就行...)

代码如下:

#include <cstdio>
#include <cstdlib>
int N;
int v[21][21];
int f[41][21][21][21];
int max(int a,int b){return a>b?a:b;}
void dp(){
    for(int x=1;x<=2*N-2;x++){
        for(int i=1;i<=x+1&&i<=N;i++){
            for(int j=1;j<=x+1&&j<=N;j++){
                for(int k=1;k<=x+1&&k<=N;k++){
                    int add;
                    if(i==j&&j==k)add=v[i][x-i+2];
                    if(i==j&&j!=k)add=v[i][x-i+2]+v[k][x-k+2];
                    if(i!=j&&j==k)add=v[i][x-i+2]+v[j][x-j+2];
                    if(i==k&&j!=k)add=v[i][x-i+2]+v[j][x-j+2];
                    if(i!=j&&j!=k&&i!=k)add=v[i][x-i+2]+v[j][x-j+2]+v[k][x-k+2];
                    f[x][i][j][k]=max(f[x-1][i-1][j][k],max(f[x-1][i][j-1][k],max(f[x-1][i][j][k-1],
                    max(f[x-1][i-1][j-1][k],max(f[x-1][i-1][j][k-1],max(f[x-1][i][j-1][k-1]
                    ,max(f[x-1][i-1][j-1][k-1],f[x-1][i][j][k])))))))+add;
                }
            }
        }
    }
}
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++)
            scanf("%d",&v[i][j]);
    }
    f[0][1][1][1]=v[1][1];
    dp();
    printf("%d",f[2*N-2][N][N][N]);
    return 0;
}

原文地址:https://www.cnblogs.com/Zarax/p/12041655.html

时间: 2024-11-10 07:26:22

我的DP训练计划:三取方格数的相关文章

三取方格数

描述 设有N*N的方格图,我们将其中的某些方格填入正整数,而其他的方格中放入0. 某人从图得左上角出发,可以向下走,也可以向右走,直到到达右下角. 在走过的路上,他取走了方格中的数.(取走后方格中数字变为0)此人从左上角到右下角共走3次,试找出3条路径,使得取得的数总和最大. 格式 输入格式 第一行:N (4<=N<=20)接下来一个N*N的矩阵,矩阵中每个元素不超过80,不小于0 输出格式 一行,表示最大的总和. 样例1 样例输入1[复制] 4 1 2 3 4 2 1 3 4 1 2 3 4

【动态规划】Vijos P1143 三取方格数

题目链接: https://vijos.org/p/1143 题目大意: NxN的矩阵,每个值只能取一次,从(1,1)走到(n,n)走三次能取得的最大值. 题目思路: [动态规划] f[x1][y1][x2][x3]表示第一次走x1,y1,相同步数下第二次走x2,y2,第三次走x3,y3的最大值. 因为步数一样y2,y3可以直接求出来. 1 // 2 //by coolxxx 3 //#include<bits/stdc++.h> 4 #include<iostream> 5 #i

begin.BZOJ 1383: 三取方格数

题目链接:传送门 题目大意:给你一个矩阵,每个格子有一个值,现在你要从左上角走到右下角(走3次),使得经过路径的权值和最大. 每个格子的值只能取一次,取完后变为0,输出走完三次后最大的权值和. 题目思路:费用流做法,对于每个格子拆点,因为权值只有第一次能取,所以将每个格子拆为两条边,一条边容量为1,费用为格子的权值,另一条边容量2,费用0.  相邻格子间连边,容量3,费用0.再建立源点S 与左上角第一个格子连边容量3,费用0.汇点 T 与右下角最后一个格子连边,容量3,费用0.跑费用流累加费用即

poj 3422 洛谷P2045 K取方格数(方格取数加强版)

Description: 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 Input: 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 Output: 一个数,为最大和 思路:仍旧是拆点 因为每个点都有一个限制K和一个价

HDU 2089 不要62(数位DP,三种姿势)

HDU 2089 不要62(数位DP,三种姿势) ACM 题目地址:HDU 2089 题意: 中文题意,不解释. 分析: 100w的数据,暴力打表能过 先初始化dp数组,表示前i位的三种情况,再进行推算 直接dfs,一遍搜一变记录,可能有不饥渴的全部算和饥渴的部分算情况,记录只能记录全部算(推荐看∑大的详细题解Orz) 代码: 1. 暴力 (以前写的) /* * Author: illuz <iilluzen[at]gmail.com> * File: 2089_bf.cpp * Create

动态规划学习系列——划分DP(三)

划分DP第三题,wikioi 1040,送我n个WA~~~ 题目大意: 这道题题述有着UVA的特色,够废话,其实就是读入一个长度最大200的字符串(不知道为何要分行输入,完全没有意义啊),分成m部分,使各部分单词量加起来最大 解题思路: 这题划分的部分跟乘积最大那题其实很像,状态转移方程也很容易想到: dp[i][k]=max(dp[i][k],dp[j][k-1]+scnt[j+1][i]) ( j >= k-1 ) scnt数组:scnt[j][i] 表示区间 j~i 所包含的单次总数 接下

VBA取行列数,运行CMD,数组,VBA写文件相关

备忘用: 1.VBA取行列数: 例如要取第二行使用了多少列:Cells(2, Columns.Count).End(xlToLeft).Column:解释:主要是End方法,VBA中如下阐述:返回一个 Range 对象,该对象代表包含源区域的区域尾端的单元格.等同于按键 End+ 向上键.End+ 向下键.End+ 向左键或 End+ 向右键.Range 对象,只读.意思就是说取到选择区域的最后一个单元格. 如果你需要取到sheet页内使用到的最大行和列,可以如下使用:    nRow = Ac

一个网站的诞生03--抓取评论数最多的一万家餐厅

在大众点评网上,有很多种方式对餐厅进行排序,比如http://www.dianping.com/search/category/1/10/o10,是上海全市按照评论总数最多对餐厅进行排序,下面有50个分页,也就是上海历年累计评论综述最多的750家餐厅.但只有750家,少了点.上海有18个区,逐区点击的话,每区都会显示前750家餐厅,比如这个http://www.dianping.com/search/category/1/10/r802o10,是浦东新区八佰伴地段的前750家.上海现在有十万家餐

addEventListener中的第三个参 数

addEventListener中的第三个参 数是useCapture, 一个bool类型.当为false时为冒泡获取(由里向外),true为为捕获 capture方式(由外向里). function addEvent(obj,sEv,fn){ if(obj.addEventListener){ obj.addEventListner(sEv,fn,false); }else{ obj.attatchEvent('on'+sEv,fn); } } <!doctype html> <htm