本文主要是介绍shuoj-小6的多米诺骨牌-双向dp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Description
小6有一副多米诺骨牌,它们的高度不一,且不计厚度。
小6将这些骨牌从左到右排成一排立起来,如果向左或者向右推倒其中一个骨牌,那么它碰到左边或者右边的骨牌会一起连续倒下。
也就是说,每个骨牌只能向左或者向右倒下。
小6想知道,最少需要直接推倒多少骨牌,才能把所有的骨牌全都放倒。
Input
第一行是一个整数T,表示数据组数。(T≤20)
每组输入中的第一行为多米诺骨牌数量N(1≤N≤100),
接着有N行,第i行有两个正整数Pi、Hi分别代表多米诺骨牌的横坐标和该骨牌的高度,数据保证每个坐标最多只有一个骨牌,并且所给出的Pi是递增的。
(1≤Pi, Hi≤1000)
Output
对于每组输入,在一行中输出一个整数,表示要把所有骨牌直接或间接放倒最少需要直接推倒的骨牌数量。
Sample Input
Sample Output
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;
}//自己的理解
#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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!