题目来自FZU2163 多米诺骨牌
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
Vasya非常喜欢排多米诺骨牌。他已经厌倦了普通的多米诺骨牌,所以他用不同高度的多米诺骨牌。他从左边到右边,把n个多米诺骨牌沿一个轴放在桌子上。每个多米诺骨牌垂直于该轴,使该轴穿过其底部的中心。第i个多米诺骨牌具有坐标xi与高度hi。如今Vasya想要知道,对于每个多米诺骨牌假设他推倒的话,右側会有多少个多米诺骨牌也会倒下。
想想看,一个多米诺倒下,假设它严格的触动右側的多米诺骨牌,被触碰的也会倒下。换句话说,假设多米诺骨牌(初始坐标x和高度h)倒下,会导致全部在[ X + 1,x + H - 1]范围内的多米诺骨牌倒下。
Input
输入有多组測试数据,处理到文件结尾。
每组測试数据第一行包括整数n(1≤N≤10^5),这是多米诺骨牌的数量。然后n行,每行包括两个整数xi与hi(-10^8≤xi≤10^8 ,2 ≤hi≤108),xi表示多米诺骨牌的坐标和hi表示多米诺骨牌的高度。没有两个多米诺骨牌在同一个坐标点上。
Output
对于每组数据输出一行,包括n个空格分隔的数Zi - 表示倒下的多米诺骨牌数量,假设Vasya推第i个多米诺骨牌(包括多米诺骨牌本身)。
Sample Input
Sample Output
第一次比赛中出现中文题目,真心认为高兴啊,无需百度翻译了……
这题目在比赛的时候我并没有思路,最后也没有做这题,赛后才思考这到题,一開始的想法就是动态规划,认为是在可接触范围内的
从后面開始,dp[i]=max{1+dp[x]),x为i~i+h-1中有多米骨牌的坐标。
可是这样一想,dp开的数组太大了(10^8),且这 dp效率明显不高啊!由于这个范围内着到有坐标,明显会超时。
后来换一种思维来看,我们先依照横坐标排序,并又一次编号,能推倒的多米诺牌的最后一个,肯定是不能推倒的那个的前一个,所以找到不能推倒的,再回溯回去。
这样非常自然,就会使用栈结构了,我们用排序后的第一张牌压入栈,我们按新编号第二张牌開始去遍历,当前牌是栈顶部元素无法接触的时候,那么栈顶部能推倒的是这张牌的新序号减上栈顶部的新序号,而且非常有可能栈中其它元素能触碰到这张牌(也依照刚刚的推断方法,能否触碰到当前牌,能则继续pop)。当能触碰到,则继续压入栈。
即是:仅仅在能够确定到不能触碰的时候才看開始考虑之前的牌!
所以,我们要引入一张无法触碰的,x坐标在无穷远(1<<30),这样,每一个牌肯定会找到无法触碰的牌(最坏情况就是找到引入的牌)
我写了两个代码,一个是以类的形式来写,还有一个是直接用数组,两者来说,第一个比較好理解。
类的形式:
#include<iostream> #include<cstdio> #include<vector> #include<stack> #include<cstring> #include<algorithm> using namespace std; int n; class GP{ public: int id; int s_id; // sort id int x; //横坐标 int h; //高度 GP(){}; bool operator <(const GP &g)const{ return this->x<g.x; } void operator =(const GP &g){ this->id=g.id; this->s_id=g.s_id; this->x=g.x; this->h=g.h; } }; vector<GP> v; //骨牌 stack<GP> stk; //栈结构 int ans[100005];//结果数组 int main(){ int i; while(scanf("%d",&n)!=EOF){ v.clear(); while(!stk.empty()){stk.pop();} v.resize(n+1); for(i=0;i<n;i++){ v[i].id=i; scanf("%d%d",&v[i].x,&v[i].h); } v[n].x=200000001; //无穷远 v[n].h=0; v[n].s_id=n; sort(v.begin(),v.end()); for(i=0;i<n;i++){ v[i].s_id=i; } stk.push(v[0]); GP tmp; for(i=1;i<=n;i++){ while(!stk.empty()&&stk.top().x+stk.top().h-1<v[i].x){ //直到无法触及的多米诺 tmp=stk.top(); stk.pop(); // cout<<tmp.x<<" "<<tmp.s_id<<" "<<v[i].s_id<<endl; ans[tmp.id]=v[i].s_id-tmp.s_id; } stk.push(v[i]); } for(i=0;i<n-1;i++){ printf("%d ",ans[i]); } printf("%d ",ans[n-1]); } return 0; }
数组形式:
#include<iostream> #include<stack> #include<algorithm> #include<cstdio> using namespace std; #define N 100005 int id[N],x[N],h[N],ans[N]; bool CMP (int a,int b){ return x[a]<x[b]; } stack<int> stk; int main(){ int n,i; while(scanf("%d",&n)!=EOF){ while(!stk.empty()) stk.pop(); for(i=0;i<n;i++){ scanf("%d%d",x+i,h+i); id[i]=i; } x[n]=1<<30; id[n]=n; sort(id,id+n,CMP); stk.push(0); for(i=1;i<=n;i++){ while(!stk.empty()&&x[id[stk.top()]]+h[id[stk.top()]]-1<x[id[i]]){ //无法触碰的多米诺 int j=stk.top(); stk.pop(); ans[id[j]]=i-j; } stk.push(i); } for(i=0;i<n-1;i++){ printf("%d ",ans[i]); } printf("%d ",ans[n-1]); } return 0; }