ZOJ 3650 Toy Blocks(DP + 线段树优化转移)
2017-08-07 22:01
453 查看
题目链接:Click here~~
题意:
给出 n 个多米诺骨牌的 X 坐标以及高度 H,每次你可以选任意一张向左推或向右推,推倒后,会产生连锁反应,问最少几次能把所有的骨牌推倒。
解题思路:
先预处理出每张牌向 左/右 推能推到的最远位置,记为 l[i] 和 r[i]。这步可以通过递推的方法在均摊总复杂度为 O(n) 下得到。
然后 dp[i][dir] 表示前 i 张牌推倒 && 第 i 张牌倒的方向为 dir 的最少次数。
如果循环方向为从 1->n 的话,dp[i][0] = min(dp[i][l[i]-1]) + 1。这个应该不难想出来,因为 dp[i-1][dir] <= dp[i][dir] ,所以一定能推越远越好。
dp[i][1] = min ( min{dp[j][1]} , min(dp[i-1]) + 1 ) (j < i && r[j] >= i)。
即,如果向右倒的话,考虑是别人推倒自己,还是自己推倒自己。{ }内的部分要用线段树优化一下,使转移的复杂度为 O(logn)。
好像存在O(1)的转移写法,但是看不懂。还有就是{ }内的部分可以用树状数组来实现
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 3;
pair<int,int> a
;
int l
,r
,dp
[2];
void pre(int n)
{
for(int i=1;i<=n;i++)
l[i] = r[i] = i;
for(int i=2;i<=n;i++)
{
int k = i;
while(l[k] != 1 && a[i].first-a[l[k]-1].first <= a[i].second)
k = l[k] - 1;
l[i] = l[k];
}
for(int i=n-1;i>=1;i--)
{
int k = i;
4000
while(r[k] != n && a[r[k]+1].first-a[i].first <= a[i].second)
k = r[k] + 1;
r[i] = r[k];
}
}
inline void check_min(int &a,int b){
a = a < b ? a : b;
}
#define lson u<<1
#define rson u<<1|1
struct Seg
{
int l,r;
int mmin,lazy;
inline int mid(){
return l + r >> 1;
}
}T[N<<2];
void build(int u,int l,int r)
{
T[u].l = l , T[u].r = r;
T[u].mmin = T[u].lazy = N;
if(l == r - 1)
return ;
int m = T[u].mid();
build(lson,l,m);
build(rson,m,r);
}
void push_up(int u)
{
T[u].mmin = min(T[lson].mmin,T[rson].mmin);
}
void push_down(int u)
{
if(T[u].lazy == N)
return ;
check_min(T[lson].mmin,T[u].lazy);
check_min(T[rson].mmin,T[u].lazy);
check_min(T[lson].lazy,T[u].lazy);
check_min(T[rson].lazy,T[u].lazy);
T[u].lazy = N;
}
void updata(int u,int l,int r,int up)
{
if(T[u].l == l && T[u].r == r)
{
check_min(T[u].mmin,up);
check_min(T[u].lazy,up);
return ;
}
push_down(u);
int m = T[u].mid();
if(r <= m)
updata(lson,l,r,up);
else if(l >= m)
updata(rson,l,r,up);
else
updata(lson,l,m,up) , updata(rson,m,r,up);
push_up(u);
}
int query(int u,int l)
{
if(T[u].l == l && T[u].r == l+1)
return T[u].mmin;
push_down(u);
int m = T[u].mid();
if(l < m)
return query(lson,l);
else
return query(rson,l);
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].first,&a[i].second);
sort(a+1,a+1+n);
pre(n);
build(1,1,n+1);
dp[0][0] = dp[0][1] = 0;
for(int i=1;i<=n;i++)
{
dp[i][0] = min(dp[l[i]-1][0],dp[l[i]-1][1]) + 1;
dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + 1;
check_min(dp[i][1],query(1,i));
updata(1,i,r[i]+1,dp[i][1]);
}
printf("%d\n",min(dp
[0],dp
[1]));
}
return 0;
}
题意:
给出 n 个多米诺骨牌的 X 坐标以及高度 H,每次你可以选任意一张向左推或向右推,推倒后,会产生连锁反应,问最少几次能把所有的骨牌推倒。
解题思路:
先预处理出每张牌向 左/右 推能推到的最远位置,记为 l[i] 和 r[i]。这步可以通过递推的方法在均摊总复杂度为 O(n) 下得到。
然后 dp[i][dir] 表示前 i 张牌推倒 && 第 i 张牌倒的方向为 dir 的最少次数。
如果循环方向为从 1->n 的话,dp[i][0] = min(dp[i][l[i]-1]) + 1。这个应该不难想出来,因为 dp[i-1][dir] <= dp[i][dir] ,所以一定能推越远越好。
dp[i][1] = min ( min{dp[j][1]} , min(dp[i-1]) + 1 ) (j < i && r[j] >= i)。
即,如果向右倒的话,考虑是别人推倒自己,还是自己推倒自己。{ }内的部分要用线段树优化一下,使转移的复杂度为 O(logn)。
好像存在O(1)的转移写法,但是看不懂。还有就是{ }内的部分可以用树状数组来实现
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 3;
pair<int,int> a
;
int l
,r
,dp
[2];
void pre(int n)
{
for(int i=1;i<=n;i++)
l[i] = r[i] = i;
for(int i=2;i<=n;i++)
{
int k = i;
while(l[k] != 1 && a[i].first-a[l[k]-1].first <= a[i].second)
k = l[k] - 1;
l[i] = l[k];
}
for(int i=n-1;i>=1;i--)
{
int k = i;
4000
while(r[k] != n && a[r[k]+1].first-a[i].first <= a[i].second)
k = r[k] + 1;
r[i] = r[k];
}
}
inline void check_min(int &a,int b){
a = a < b ? a : b;
}
#define lson u<<1
#define rson u<<1|1
struct Seg
{
int l,r;
int mmin,lazy;
inline int mid(){
return l + r >> 1;
}
}T[N<<2];
void build(int u,int l,int r)
{
T[u].l = l , T[u].r = r;
T[u].mmin = T[u].lazy = N;
if(l == r - 1)
return ;
int m = T[u].mid();
build(lson,l,m);
build(rson,m,r);
}
void push_up(int u)
{
T[u].mmin = min(T[lson].mmin,T[rson].mmin);
}
void push_down(int u)
{
if(T[u].lazy == N)
return ;
check_min(T[lson].mmin,T[u].lazy);
check_min(T[rson].mmin,T[u].lazy);
check_min(T[lson].lazy,T[u].lazy);
check_min(T[rson].lazy,T[u].lazy);
T[u].lazy = N;
}
void updata(int u,int l,int r,int up)
{
if(T[u].l == l && T[u].r == r)
{
check_min(T[u].mmin,up);
check_min(T[u].lazy,up);
return ;
}
push_down(u);
int m = T[u].mid();
if(r <= m)
updata(lson,l,r,up);
else if(l >= m)
updata(rson,l,r,up);
else
updata(lson,l,m,up) , updata(rson,m,r,up);
push_up(u);
}
int query(int u,int l)
{
if(T[u].l == l && T[u].r == l+1)
return T[u].mmin;
push_down(u);
int m = T[u].mid();
if(l < m)
return query(lson,l);
else
return query(rson,l);
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].first,&a[i].second);
sort(a+1,a+1+n);
pre(n);
build(1,1,n+1);
dp[0][0] = dp[0][1] = 0;
for(int i=1;i<=n;i++)
{
dp[i][0] = min(dp[l[i]-1][0],dp[l[i]-1][1]) + 1;
dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + 1;
check_min(dp[i][1],query(1,i));
updata(1,i,r[i]+1,dp[i][1]);
}
printf("%d\n",min(dp
[0],dp
[1]));
}
return 0;
}
相关文章推荐
- ZOJ 3650 Toy Blocks(DP + 线段树优化转移)
- ZOJ 3650(多米诺骨牌 dp + 线段树优化)
- ZOJ 3650 Toy Blocks(线段树+DP)
- zoj 3349 dp + 线段树优化
- POJ 1769 Minimizing maximizer(最少区间覆盖dp + 线段树优化查询区间最小值)
- zoj 3349 Special Subsequence(dp+线段树优化)
- zoj 3349 简单DP 线段树或树状数组优化
- ZOJ Toy Blocks
- ZOJ 3349 Special Subsequence(线段树优化DP)
- ZOJ 3349 Special Subsequence(DP+线段树优化)
- zoj 2900 DP(线段树优化)
- ZOJ 3349 Special Subsequence(LIS+线段树优化)
- ZOJ-3349 Special Subsequence 线段树优化DP
- CodeVS 2245 浅谈二维线段树优化间距限制型LCS动态规划状态转移
- ZOJ 3349 Special Subsequence 简单DP + 线段树
- 【BZOJ-1010】玩具装箱toy DP + 斜率优化
- ZOJ 3349 Special Subsequence(DP+线段树优化)
- HNOI 2008 玩具装箱toy(DP + 斜率优化)
- 864E(dp + 线段树优化)
- ZOJ 3349 Special Subsequence【dp+线段树优化】