摩天大楼(dom.pas/c/cpp)
题目大意
在数轴上有 N 栋摩天大楼,第 i 栋的位置为 Xi,高度为 Hi,现在你需要推倒所有的摩
天大楼。你每次可以选择炸毁一个大楼使得它往左边倒下去,或者往右边倒下去,在一栋摩
天大楼倒下去的时候,有可能顺带推倒了一些其他的摩天大楼。第 i 栋大楼向左倒会将所有
在它左边并且满足|Xi-Xj|<=Hi 的摩天大楼同时向左推倒,并且这些摩天大楼在倒下去的时
候有可能带动其他的大楼继续倒下(向右同理) 。
现在问最少需要炸多少次摩天大楼才能使得所有的摩天大楼均被推倒。
输入文件
输入文件为 dom.in。
第一行一个整数 N,表示摩天大楼的个数。
以下 N 行每行两个整数 Xi,Hi,表示第 i 栋摩天大楼位置以及它的高度。
输入保证 Xi 递增输入。
输出文件
输出文件为 dom.out。
输出一行一个整数代表最少炸毁的次数。
样例输入
6
1 1
2 2
3 1
5 1
6 1
8 3
样例输出
2
数据规模与约定
对于 30%的数据,N≤10;
对于 50%的数据,N≤1000;
对于 100%的数据,N≤100000,Hi≤10^8,0≤Xi≤2^31。
————————————————————————————————题解
我们求出l[x],r[x]表示把第x座往左推能倒多少座,第x座往右推能推倒多少座
更新时dp[x+r[x]-1]=min(dp[x-1]+1) (这个要先更新因为先更新l[x]会影响到后面,造成一栋楼往左推和往右推同时更新)
往左推要找dp[x-l[i]+1]-dp[x-1]中最小的一个,所以要再单调栈上二分
同理寻找l[x]和r[x]也要找到延伸最远的一个,单调栈上二分
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring>//important 4 #include <algorithm> 5 #include <vector> 6 #define siji(i,x,y) for(int i=(x);i<=(y);++i) 7 #define gongzi(j,x,y) for(int j=(x);j>=(y);--j) 8 #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i) 9 #define sigongzi(j,x,y) for(int j=(x);j>(y);--j) 10 #define ivorysi 11 #define inf 0x7f7f7f7f 12 typedef long long ll; 13 using namespace std; 14 int l[100005],r[100005],pos[100005],h[100005],dp[100005],n; 15 vector<int> st; 16 int binary(int poi,int on) { 17 int l=0,r=st.size(),mid; 18 while(l<r) { 19 mid=(l+r)/2; 20 if(on*pos[st[mid]]>=on*poi) r=mid; 21 else l=mid+1; 22 } 23 return r>=st.size() ? -1 : r; 24 } 25 void init() { 26 scanf("%d",&n); 27 siji(i,1,n) { 28 scanf("%d%d",&pos[i],&h[i]); 29 } 30 pos[0]=-1000000;//寻找1-l[1]时就是-1 31 st.clear(); 32 siji(i,1,n) { 33 int x=binary(pos[i]-h[i],1); 34 if(x!=-1)l[i]=i-st[x]+l[st[x]]; 35 else l[i]=1; 36 while(st.size()>=1 && 37 st[st.size()-1]-l[st[st.size()-1]]>=i-l[i])//不是l[i]越大越好,而是看它往左倒可以延伸到哪里 38 st.pop_back(); 39 st.push_back(i); 40 } 41 st.clear(); 42 gongzi(i,n,1) { 43 int x=binary(pos[i]+h[i],-1); 44 if(x!=-1) r[i]=st[x]-i+r[st[x]]; 45 else r[i]=1; 46 while(st.size()>=1 && 47 st[st.size()-1]+r[st[st.size()-1]]<=i+r[i]) 48 st.pop_back(); 49 st.push_back(i); 50 } 51 memset(dp,inf,sizeof(dp)); 52 dp[0]=0; 53 } 54 void solve() { 55 init(); 56 st.clear(); 57 st.push_back(0); 58 59 siji(i,1,n) { 60 dp[i+r[i]-1]=min(dp[i+r[i]-1],dp[i-1]+1); 61 int x=binary(pos[i-l[i]],1);//找到i-l[i]-1之前的一个最大的,所以寻找pos[i-l[i]] 62 if(x!=-1) dp[i]=min(dp[i],dp[st[x]]+1); 63 else dp[i]=min(dp[i],dp[i-1]+1); 64 while(st.size()>=1 && 65 dp[st[st.size()-1]]>=dp[i]) 66 st.pop_back(); 67 st.push_back(i); 68 } 69 printf("%d ",dp[n]); 70 } 71 int main(int argc, char const *argv[]) 72 { 73 #ifdef ivorysi 74 freopen("dom.in","r",stdin); 75 freopen("dom.out","w",stdout); 76 #else 77 freopen("f1.in","r",stdin); 78 #endif 79 80 solve(); 81 return 0; 82 }