T1.多米诺骨牌(card)
小 Z 最近买了很多很多的多米诺骨牌,他选出了其中的一些排成了一排,并
且准备从右到左碰倒这些骨牌。每个骨牌有一个坐标 xi(>=1)和一个大小 yi(>=1),
倒下时将会碰倒坐标区间位于[xi-yi,xi)内的所有骨牌。当然没有两个骨牌有相同
的坐标, 并且小 Z 规定坐标大的更靠右。
但是他发现他买的骨牌太巨了,所以在倒下的时候会将所有碰倒的骨牌破坏
掉,被破坏掉的骨牌就无法使用了,并且不会倒下。得知这个消息的小 Z 十分惊
讶,他想知道如果还按刚才这种方法从右到左碰倒所有没被破坏的骨牌,将有多
少个骨牌被破坏。这个问题对你来说太简单啦,所以小 Z 又改了主意,他现在想
知道,如果他可以在所有骨牌的严格右边任意位置放置一个任意大小的骨牌,最
少有多少个骨牌会被破坏?
[输入格式]
从 card.in 中读取数据。
第一行读入一个数字 n,表示小 Z 已经摆放的骨牌数量。
接下来 n 行,每行读入两个正数 xi,yi,表示一个骨牌的信息。
[输出格式]
输出一个数字,表示最少有多少个骨牌被破坏。
[样例输入]
4
1 9
3 1
6 1
7 4
[样例输出]
1
[样例解释]
假如在位置 666 摆放一个大小 659 的骨牌,将会只有一个骨牌被破坏。
[数据范围与约定]
对于 20%的数据 保证存在一个最优方案在坐标[1,100]内摆放骨牌
对于 40%的数据 n<=5000
对于 100%的数据 n<=100000,1<=xi,yi<=10^9
题解:定义f[i]表示从第i个骨牌开始推,没被摧毁的骨牌数,f[i]=f[j]+1(j为推倒i后没被摧毁的最右边的纸牌),直接搜索复杂度是n^2,但是我们二分i前的点求出j,复杂度就降为nlog(n)了。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define MN 100005 5 #define INF 0x7fffffff 6 using namespace std; 7 int n,f[MN],ans; 8 struct node{int x,y;}c[MN]; 9 bool cmp(node a,node b){return a.x<b.x;} 10 int check(int u){ 11 int l=0,r=u-1,len=c[u].x-c[u].y,ret; 12 while(l<=r){ 13 int mid=(l+r)/2; 14 if(c[mid].x<len) ret=mid,l=mid+1; 15 else r=mid-1; 16 } 17 return ret; 18 } 19 int main() 20 { 21 freopen("card.in","r",stdin); 22 freopen("card.out","w",stdout); 23 scanf("%d",&n); 24 for(int i=1;i<=n;i++) scanf("%d%d",&c[i].x,&c[i].y); 25 sort(c+1,c+1+n,cmp); c[0].x=-INF; 26 for(int i=1;i<=n;i++){ 27 int now=check(i); 28 f[i]=f[now]+1; 29 ans=max(ans,f[i]); 30 } 31 printf("%d",n-ans); 32 return 0; 33 }
题解:首先,相邻2数的f函数必定最大,所以我们不用考虑位置相差大于1的情况;那么,我们把每个位置的差值求出来,维护一个单调栈,每次更新答案即可。
PS: 单调栈学习:http://blog.csdn.net/liujian20150808/article/details/50752861
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #define MN 100005 4 using namespace std; 5 int n,m,q[MN],top,l,r,a[MN],b[MN]; 6 long long s[MN],ans; 7 int mabs(int x){return x>0?x:-x;} 8 int main() 9 { 10 freopen("array.in","r",stdin); 11 freopen("array.out","w",stdout); 12 scanf("%d%d",&n,&m); 13 for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=mabs(a[i]-a[i-1]); 14 while(m--){ 15 scanf("%d%d",&l,&r); 16 q[top=0]=l; ans=0; 17 for(int i=l+1;i<=r;i++){ 18 while(top&&b[i]>b[q[top]]) top--; 19 q[++top]=i; s[top]=s[top-1]+1LL*(q[top]-q[top-1])*b[q[top]]; 20 ans+=s[top]; 21 } 22 printf("%lld ",ans); 23 } 24 return 0; 25 }
T3.小 Z 打怪兽(atm)
小 Z 最近在玩一个叫做“奥特曼打怪兽”的游戏,在游戏里小 Z 扮演一个强力
奥特曼来抵挡怪兽的入侵。
聪明的小 Z 在怪兽来之前就已经摸清了所有怪兽的信息。总共有 n 只怪兽,
和第 i 只怪兽战斗会使小 Z 损失 ai 点生命值,但是击败这个怪兽之后小 Z 可以
获得一个血药, 为自己回复 bi 点生命值。小 Z 的人物初始只有 h 点生命值,并
且一旦生命值降到 0 或者更低,小 Z 的奥特曼就会死亡。所以小 Z 向你求助,
希望你能告诉他一个合法的打怪顺序,可以击败所有怪兽并且不死亡。
[输入格式]
从 atm.in 中读取数据。
第一行读入两个数字 n,h,表示有 n 个怪兽,小 Z 的角色的初始血量是 h。
接下来 n 行,每行两个数字 ai,bi 描述一个怪兽的信息。
[输出格式]
如果不存在合法的打怪序列,输出-1。
否则输出一行 n 个数字构成的一个 1 到 n 的排列, 表示一个合法的打怪顺序,
第 i 个数表示打的第 i 只怪兽的编号。
如果有多个答案,你可以输出任意一个。
[样例输入]
3 5
3 1
4 8
8 3
[样例输出]
2 3 1
[数据范围与约定]
本题采用子任务制,只有你通过一个 subtask 内所有的数据点才能得到这个数据
点对应的分数。
Subtask1 包含 20 Points 满足 n<=10
Subtask2 包含 20 Points 满足 n<=20
Subtask3 包含 30 Points 满足 n<=5000
Subtask4 包含 30 Points 满足 n<=200000
所有数据满足 0<=ai,bi<=1000 h<=10^6
题解:没写
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #define MN 200005 5 using namespace std; 6 int n,h,ans[MN],cnt; 7 struct node{ 8 int a,b,num,ch; 9 friend bool operator <(const node&a,const node&b){ 10 return a.b<b.b; 11 } 12 }un[MN]; 13 priority_queue<node> pq; 14 int main() 15 { 16 freopen("atm.in","r",stdin); 17 freopen("atm.out","w",stdout); 18 scanf("%d%d",&n,&h); 19 for(int i=1;i<=n;i++){ 20 node x; scanf("%d%d",&x.a,&x.b); 21 x.ch=x.a-x.b; x.num=i; pq.push(x); 22 } 23 for(int i=1;i<=n;i++){ 24 node x=pq.top(); cnt=0; 25 while(x.a>=h&&!pq.empty()){ 26 pq.pop(); un[++cnt]=x; 27 if(!pq.empty()) x=pq.top(); 28 } 29 if(pq.empty()){printf("-1");return 0;} 30 x=pq.top(); ans[i]=x.num; pq.pop(); h-=x.ch; 31 for(int j=cnt;j>=1;j--) pq.push(un[j]); 32 } 33 for(int i=1;i<=n;i++) printf("%d ",ans[i]); 34 return 0; 35 }