题目描述
B国大举进犯,A国根据地利设下埋伏获得全胜,战后A国王论功行赏,现在问题来了。
现在我们将问题简化,已知B国一共来了M个士兵,每个士兵的生命值为Hi,A国设置了N个据点,当士兵经过第j个据点时,生命值减少Wj,当士兵的生命值小于等于0时就会被当前这个据点杀死。由于地形原因士兵必须依次经过据点1,据点2,据点3. . . . . .据点N。
现在A国王想知道前M个据点的杀敌数为多少。
输入
多组输入。
对于每组数据,首先输入两个整数N,M(1 <= N <= 100000,1 <= M<= min(N, 10000))。
接下来的M行,每行一个整数Hi,代表第i个士兵的初始生命值。(1 <= Hi <= 10000000)
接下来的N行,每行一个整数Wj,代表第j个据点的攻击力。(0 <= Wi <= 10000)
输出
对于每组数据。
输出M行,每行一个整数代表第j个据点的杀敌数。
示例输入
3 3 1 2 3 1 1 1
示例输出
1 11
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int hi[1111111],wi[1111111],over[1111111]; int main() { int n,m,i,j; while(scanf("%d%d",&n,&m)!=EOF) {for(i=0;i<m;i++) over[i]=0; for(i=0;i<m;i++) scanf("%d",&hi[i]); sort(hi,hi+m); for(i=0;i<n;i++) { scanf("%d",&wi[i]); wi[i]=(i==0?wi[i]:wi[i-1]+wi[i]); } for(i=0,j=0;i<m&&j<m;) {if(wi[i]>=hi[j]) {over[i]++;/*printf("%d %d\n",over[i],i);*/ j++;} else i++; } for(i=0;i<m;i++) printf("%d\n",over[i]); } return 0;}
刚开始题意理解错了,等到理解之后发现累加比较麻烦,正巧一博客有此题,第18行的三目运算法用的很巧妙。士兵生命值需要排序,但是感觉题目没提及。
另刚开始用memset重置over,发现对于比较大的数组很浪费时间和内存,反而用for循环更好。
对这道题来用for节约了大概100ms时间和4000K左右的内存。
还有一种更简洁的办法,就是用二分查找的原理,通过lower_bound()这个函数来查找士兵的生命值第一次小于等于据点攻击力的位置,从而使该据点的杀敌数加一。思路来自SDUT。#include <iostream> #include <cstring> #include <algorithm> using namespace std; int h[1000000],w[1000000],cont[1000000]; int main() { ios::sync_with_stdio(false); int n,m,i; while(cin>>n>>m) { memset(cont,0,sizeof(cont)); for(i=0;i<m;i++) cin>>h[i]; for(i=0;i<n;i++) { cin>>w[i]; if(i!=0) w[i]+=w[i-1]; } for(i=0;i<m;i++) { int x=lower_bound(w,w+n,h[i])-w; cont[x]++; } for(i=0;i<m;i++) cout<<cont[i]<<endl; } return 0; }