shuoj-小6的多米诺骨牌-双向dp

2023-12-22 17:32

本文主要是介绍shuoj-小6的多米诺骨牌-双向dp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Description

6有一副多米诺骨牌,它们的高度不一,且不计厚度。

6将这些骨牌从左到右排成一排立起来,如果向左或者向右推倒其中一个骨牌,那么它碰到左边或者右边的骨牌会一起连续倒下。

也就是说,每个骨牌只能向左或者向右倒下。

6想知道,最少需要直接推倒多少骨牌,才能把所有的骨牌全都放倒。

Input

第一行是一个整数T,表示数据组数。(T20)

每组输入中的第一行为多米诺骨牌数量N(1≤N≤100),

接着有N行,第i行有两个正整数Pi、Hi分别代表多米诺骨牌的横坐标和该骨牌的高度,数据保证每个坐标最多只有一个骨牌,并且所给出的Pi是递增的。

(1Pi, Hi1000)

Output

对于每组输入,在一行中输出一个整数,表示要把所有骨牌直接或间接放倒最少需要直接推倒的骨牌数量。

Sample Input

2
3
1 3
4 2
5 2
3
1 3
4 3
7 7

Sample Output

2
1
题解::自认为这道题的解法很难想明白,从第一次见到这个题也有几个月了。直到前几天看到题解才大概有了一点感觉,今天想这道题想了很久,也演示了几遍,终于搞清楚了一点。这道题的思路是这样的:
(这里输入骨牌从0开始,dp从1开始)用dp[ i ]记录第 i 个骨牌最少能被几次推到,这样dp[ n ]就是要求的结果。更新的原则是: 从左向右遍历一遍骨牌,先从左向右更新当前骨牌能够放倒的所有骨牌,然后从右更新(更新的骨牌为能够从右向左砸到当前骨牌的牌以及能够砸到所有能被更新的骨牌的所有骨牌)。在这里举个例子(1,3)(2,4)(5,2)(7,6)(10,1)(11,3)当遍历第一个节点(1,3)的时候从左更新会更新(1,3)(2,4)(5,2)【这里不解释】,从右更新会更新(1,3)(2,4)(7,6),【这里第二张(2,4)能够砸倒(1,3)更新第二张,(7,6)能够砸倒(2,4)更新第三张,(10,1)不能砸倒(7,6)不更新第四张,就是这样的原则。
首先要把dp[ ] 初始化为无穷大INF,在从左向右方向的过程中状态转移方程为dp[ j +1]  =  min(dp[ j +1] , dp[ i ]+1) , 其中j为要更新的牌,i为当前遍历到的第几张。在p[j]+在从右向左方向的过程中状态转移方程为dp[ j +1] = min(dp[ j +1],dp[ i ]+1);

这里给出一组样例,以及输出,看了程序后跟着程序一步一步的走就明白意思了。

1 
6
1 3
4 2
5 2
7 6
10 1
11 3
代码::

#include <bits/stdc++.h>
#include <ext/hash_map>
#include <ext/hash_set>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/priority_queue.hpp>
using namespace __gnu_pbds;
using namespace std;
#define INF 0x3f3f3f3f
#define PB push_back
#define MP make_pair
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define RIT reverse_iterator
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> PII;
typedef vector<int> VI;
typedef vector<PII> VII;
#define PQ std::priority_queue
#define HEAP __gnu_pbds::priority_queue
#define X first
#define Y second
#define lson l,m,o<<1
#define rson m+1,r,o<<1|1
/********************************
************头文件***************
********************************/
int p[110];
int h[110];
int dp[110];int main() {ios::sync_with_stdio(false);int t;cin >> t;while (t--) {int n;cin >> n;CLR(dp,INF);REP(i, n) cin >> p[i] >> h[i];dp[0] = 0;REP(i, n) {//首先从左向右倒int r = p[i] + 1;//要确保在跟自己进行比较的时候一定会把自己推到//这里要注明下面的p[x]表示第x+1块多米诺牌,dp[x]表示第x块多米诺牌最少第几次被推倒REP2(j, i, n-1) {if (p[j] < r) {//说明可以推到;p[j]和下面的dp[j+1]指的是一块牌dp[j+1] = min(dp[j+1], dp[i]+1);//这里应用的很巧妙,当i==j是必然是可以进入循环的//如果dp[j+1]没有被更新过,说明以前不会被推倒,它被推倒的次数最小值就会比上一张牌多一,//但是如果被更新过,说明上一张牌可以把这一张砸到,那么dp[i]==dp[j+1],所以最小值仍为dp[j+1]//当j>i的时候要进入循环必然是能被砸到的,这时候dp[j+1]肯定应该等于dp[j]//这里很难想明白,可以把下面这句话输出来,很有助于理解//cout<<'1'<<" "<<j+1<<" "<<dp[j+1]<<endl;r = max(r, p[j]+h[j]);//这里r表示的是:从i到j可以砸倒的最大位置} else break;}//相比于上面这里要简单一些int l = p[i];REP2(j, i, n-1) {if (p[j] - h[j] < l) {dp[j+1] = min(dp[j+1], dp[i]+1);//理解了上面的,这里就好懂了//可以把这句话输出出来看结果//cout<<'2'<<" "<<j+1<<" "<<dp[j+1]<<endl;l = p[j];//通过更新l逐一找到能将i砸到的牌x,以及能将x砸到的牌}}}cout << dp[n] << endl;//dp[n]便是能将第n张牌砸到的最小需要推几次了。}return 0;
}//自己的理解

这道题暂时告一段落,还需要继续学习……

2015/10/10
今天终于又看到这道题,感觉理解深刻了很多。dp的作用就是将复杂过程分解成简单的步骤地推出来。贴出新的代码,虽然没有太大的改进,但是思路变得简单了(就是原来太麻烦了……)
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 110;
int p[maxn];
int h[maxn];
int dp[maxn];int main(){int t;cin>>t;while(t--){int n;cin>>n;memset(dp,INF,sizeof(dp));/**记录节点从 1开始,思考更方便(统一dp和p,h的索引)*/for(int i = 1;i<=n;i++) cin>>p[i]>>h[i];/**初始化dp[0]是为dp[1]服务*/dp[0] = 0;for(int i = 1;i<=n;i++){/*r和l的作用是不一样的,向左和向右的思路是不一样的。因为向右为正序,更新后的r为向右推倒的最大位置。向左为逆序,l表示当前要被推到的位置。所以在向右的时候else要break,因为再进行下去已经没有意义了。而向左则不一样,即使位置较大的p也可能推到当前的位置。这样就有一个弊端,dp[i]并不一定为推到i的最小次数(好像也没什么卵用)不用管**/int r = p[i]+h[i];for(int j = i;j<=n;j++){if(p[j]<r){dp[j] = min(dp[j],dp[i-1]+1);r = max(p[j]+h[j],r);}else    break;}int l = p[i];for(int j = i;j<=n;j++){if(p[j] - h[j] < l){dp[j] = min(dp[j],dp[i-1]+1);l = p[j];}}}cout<<dp[n]<<endl;}return 0;
}





这篇关于shuoj-小6的多米诺骨牌-双向dp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/524831

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl

uva 10029 HASH + DP

题意: 给一个字典,里面有好多单词。单词可以由增加、删除、变换,变成另一个单词,问能变换的最长单词长度。 解析: HASH+dp 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o