UVa 10599

题意:

  给出r*c的网格,其中有些格子里面有垃圾,机器人从左上角移动到右下角,只能向右或向下移动。问机器人能清扫最多多少个含有垃圾的格子,有多少中方案,输出其中一种方案的格子编号。格子编号是从 左上角第一个开始,一行一行的按自然数顺序编。起始行列是第一行第一列。所以例如一个格子的行列号为(ro,co),那么它的编号为bh=(ro-1)*column+co,其中column指这个格子有多少列。(我觉得原题里面有个错误,题目叙述倒数第二行应该是41(6,6)不是41(6,7))。

题解:  

  显然,格子的编号都是递增的,每个含有垃圾的格子的编号也是递增的,要求能扫多少个有垃圾的格子,其实可以看成求这些垃圾格子编号的一个最长上升子序列(lis),而且这和普通格子没关系。需要注意的就是求lis时格子编号大的一定不能在编号小的左边,可以在同一列上。因为机器人只能向下或向右走。所以判断的时候要判断一下两个格子的列大小,这个也可以转化为比较编号的大小:(bh-1)%colum,可以算一下这个式子正好算出格子的 列号-1。当然,这里可以用其它办法判断。然后就是用dp[]数组记录最多清扫多少个格子,用save[]记录垃圾格子的编号,用num[]数组记录方案总数(就是看成普通lis求法),用patn[]数组记录当前状态是从哪里转移过来的,也就是记录路径。

详见代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = ;
int Map[maxn][maxn];//存那个格子有垃圾
int dp[maxn*maxn], num[maxn*maxn], path[maxn*maxn], save[maxn*maxn];//如上文所述
int r, c, n;//格子行、列,有多少个垃圾格子 void print(int cur)//递归输出
{
if (path[cur] != -)
print(path[cur]);
if (cur != n - || Map[r][c])//因为从n-1开始调用,当右下角格子不是垃圾格子时不需要输出(cur!=n-1),是垃圾格子时需要输出(Map[r][c])
printf(" %d", save[cur]);
} int main()
{
int cas = ;
while (cin >> r >> c)
{
memset(Map, , sizeof(Map));
memset(save, , sizeof(save));
if (r == - && c == -) break;
int a, b;
while (cin >> a >> b)
{
if (a == && b == ) break;
Map[a][b] = ;
}
n = ;
for (int i = ; i <= r; i++)
for (int j = ; j <= c; j++) {
if (Map[i][j])
save[n++] = (i - )*c + j;//为垃圾格子编号
}
if (!Map[r][c]) save[n++] = r*c;//因为最后要到达右下角,所以不管右下角是不是垃圾格子,都把它看成有,求"lis"过程好办点
for (int i = ; i <= n; i++) {//dp过程,和求lis过程类似
dp[i] = ; num[i] = ; path[i] = -;
for (int j = ; j < i; j++) {
if (((save[j] - ) % c) <= ((save[i] - ) % c)) {//比较列
if (dp[i] == dp[j] + ) {//此时相当于又多了一种到i状态(第i个数)的方案数,那么直接累加num[j]
num[i] += num[j];
}
else if (dp[i] < dp[j] + ) {//此时状态可更新
dp[i] = dp[j] + ;//更新状态
num[i] = num[j];//改为新状态的方案
path[i] = j;//由于有新的状态,所以记录到当前状态的上一个状态位置
}
}
}
}
if (!Map[r][c]) dp[n - ]--;//当右下角那个不是垃圾格子时,能清理的垃圾格子数-1
printf("CASE#%d: %d %d", cas++, dp[n - ], num[n - ]);
print(n - );//输出路径
printf("\n");
}
return ;
}

UVa 10599 code_1

   网上见到还有人用记忆化搜索来写,其实我不太懂什么叫记忆化搜素。我的理解就是在搜索的时候保存搜过的状态。dp也是保存状态,自底向上推的时候就是由小问题向大问题求解并保存状态,自顶向下就是由回溯求解过程中保存状态,觉得这样就是记忆化搜索>_<.。感觉自己对递归,回溯,深搜理解的还是不够深刻。只能写一些简单的递归、深搜,只理解到递归会层层往上返回答案。但看别人写的时候去很难想到为什么这样写,他们好像把dfs抽象了一下,到某个状态要是搜了就不管了,直接返回答案。这样中间其实很多步骤都不用完全知道,只要保证自己写的正确就行了,反观我写dfs时绝大部分时候都要知道所有的状态情况,这样写起来很累,而且容易出错,调试也麻烦。感觉自己这方面需要加强!

  说回这道题,先求能扫多少个垃圾格子,其实刚开始我也是dfs,就从dfs(1,1)开始搜呗,到(row,col)结束,但我写的总有问题。我觉得问题在没有抽象的理解递归和回溯,只知道它会一层一层往回返回答案,但其实这个返回的顺序是不一定按照自己思路里的那个顺序的而且一个位置可能重复搜好多次。所以只有抽象的理解,设定好结束状态和保存搜过的状态,按照要求写下去才会不易出错。这道题dfs求的就是最大清扫垃圾格子的数目,保存在f[1][1]。我以前的想法是走一步加一步,存最后结果的应该是f[row][col]。但这个却是存在[f1][1],利用每一层返回的值,相当于倒着往回求出结果,需要我深刻体会啊!后悔solve()求最优路径个数其实道理也差不多,num[]数组存某个点到终点的路径个数,最后也是返回结果num[1][1],也是倒着推回来。设定结束状态,到[row][col]就返回,再记忆化存结果返回,即存过的状态(num[i][j]!=-1)不用搜了。后面就是从当前点开始,满足要求的点(g[i][j]&&g[i][j]+f[i][j]==f[r][c])就再搜,并累加答案。最后路径输出也是类似的递归,判断输出的最后一个点,然后return,其他时候输出完再继续递归。

  最后,我又一次感觉到了代码好美。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = ;
int f[N][N], g[N][N], num[N][N];
int row, col, res; int dfs(int r, int c)
{
if (f[r][c] != -) return f[r][c];
if (r == row&&c == col) return f[r][c] = g[r][c];
f[r][c] = ;
if (r + <= row)
f[r][c] = max(f[r][c], dfs(r + , c) + g[r][c]);
if (c + <= col)
f[r][c] = max(f[r][c], dfs(r, c + ) + g[r][c]);
return f[r][c];
} int solve(int r, int c)
{
if (num[r][c] != -) return num[r][c];
if (f[r][c] == ) return num[r][c] = f[r][c];
num[r][c] = ;
for (int i = r; i <= row; i++)
for (int j = c; j <= col; j++)
if (g[i][j] && g[i][j] + f[i][j] == f[r][c])
num[r][c] += solve(i, j);
return num[r][c];
} void print_path(int r, int c)
{
if (g[r][c] && f[r][c] == ) {
printf(" %d\n", (r - )*col + c);
return;
}
printf(" %d", (r - )*col + c);
for(int i=r;i<=row;i++)
for(int j=c;j<=col;j++)
if (g[i][j] && g[i][j] + f[i][j] == f[r][c]) {
print_path(i, j);
return;
}
} void print()
{
for(int i=;i<=row;i++)
for(int j=;j<=col;j++)
if (g[i][j] && f[i][j] == res) {
print_path(i, j);
return;
}
} int main()
{
int cas = , a, b;
while (cin>>row>>col)
{
if (row == - && col == -) break;
memset(g, , sizeof(g));
memset(f, -, sizeof(f));
memset(num, -, sizeof(num));
while (cin>>a>>b)
{
if (a == && b == ) break;
g[a][b] = ;
}
res = dfs(, );
solve(, );
printf("Case#%d: %d %d", cas++, res, num[][]);
print();
}
return ;
}

UVa 10599 code_2

UVa 10599【lis dp,记忆化搜索】的更多相关文章

  1. uva 10891 区间dp+记忆化搜索

    https://vjudge.net/problem/UVA-10891 给定一个序列x,A和B依次取数,规则是每次只能从头或者尾部取走若干个数,A和B采取的策略使得自己取出的数尽量和最大,A是先手, ...

  2. 状压DP+记忆化搜索 UVA 1252 Twenty Questions

    题目传送门 /* 题意:给出一系列的01字符串,问最少要问几个问题(列)能把它们区分出来 状态DP+记忆化搜索:dp[s1][s2]表示问题集合为s1.答案对错集合为s2时,还要问几次才能区分出来 若 ...

  3. UVA - 10118Free Candies(记忆化搜索)

    题目:UVA - 10118Free Candies(记忆化搜索) 题目大意:给你四堆糖果,每一个糖果都有颜色.每次你都仅仅能拿随意一堆最上面的糖果,放到自己的篮子里.假设有两个糖果颜色同样的话,就行 ...

  4. 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索

    题目描述 求一棵 $[1,n]$ 的线段树的最大匹配数目与方案数. $n\le 10^{18}$ 题解 树形dp+记忆化搜索 设 $f[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配选择根 ...

  5. 【BZOJ】1415 [Noi2005]聪聪和可可 期望DP+记忆化搜索

    [题意]给定无向图,聪聪和可可各自位于一点,可可每单位时间随机向周围走一步或停留,聪聪每单位时间追两步(先走),问追到可可的期望时间.n<=1000. [算法]期望DP+记忆化搜索 [题解]首先 ...

  6. [题解](树形dp/记忆化搜索)luogu_P1040_加分二叉树

    树形dp/记忆化搜索 首先可以看出树形dp,因为第一个问题并不需要知道子树的样子, 然而第二个输出前序遍历,必须知道每个子树的根节点,需要在树形dp过程中记录,递归输出 那么如何求最大加分树——根据中 ...

  7. poj1664 dp记忆化搜索

    http://poj.org/problem?id=1664 Description 把M个相同的苹果放在N个相同的盘子里,同意有的盘子空着不放,问共同拥有多少种不同的分法?(用K表示)5.1.1和1 ...

  8. ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. Poor Ramzi -dp+记忆化搜索

    ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. ...

  9. POJ 1088 DP=记忆化搜索

    话说DP=记忆化搜索这句话真不是虚的. 面对这道题目,题意很简单,但是DP的时候,方向分为四个,这个时候用递推就好难写了,你很难得到当前状态的前一个真实状态,这个时候记忆化搜索就派上用场啦! 通过对四 ...

随机推荐

  1. 【python之路33】开发模式单例模式及简单的服务器请求框架原理

    1.单例模式指的是创建单个实例,例如:数据库连接池中包含10个数据库连接,用户访问数据时随机从连接池中拿出一个进行连接,其他用户再次访问时不再创建对象进行连接 #!usr/bin/env python ...

  2. bzoj 3231 [Sdoi2008]递归数列——矩阵乘法

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3231 矩阵乘法裸题. 1018是10^18.别忘了开long long. #include& ...

  3. Python子进程 (subprocess包)

    Python子进程 (subprocess包) subprocess以及常用的封装函数 当我们运行python的时候,我们都是在创建并运行一个进程.正如我们在Linux进程基础中介绍的那样,一个进程可 ...

  4. Codeforces Round #573 (Div. 2)

    A:Tokitsukaze and Enhancement 当时看错条件了..以为A>C>B>D.就胡写了判断条件. #include<bits/stdc++.h> us ...

  5. jeecms9自定义标签以及使用新创建的数据库表

    转载 https://blog.csdn.net/nice_meng/article/details/89179089 本系统使用的是jeecmsv9版本,收集网上知识后,进行个人汇总 首先,自己创建 ...

  6. CentOS7 安装 Nginx 1.12.1

    安装准备: nginx 依赖的一些 lib 库: yum install gcc-c++ yum install pcre pcre-devel yum install zlib zlib-devel ...

  7. python实例 输出字符串和数字

    但有趣的是,在javascript里我们会理想当然的将字符串和数字连接,因为是动态语言嘛.但在Python里有点诡异,如下: #! /usr/bin/python a=2 b="test&q ...

  8. python之pip

    sudo vim /usr/bin/lsb_release 确保第一行是python2.7,不然无法使用pip安装第三方依赖

  9. ML面试1000题系列(81-90)

    本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 81.已知一组数据的协方差矩阵P,下面关于主分量 ...

  10. Hdu 1045 二分匹配

    题目链接 Fire Net Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...