POJ 4302 Holedox Eating
Case 3: 2
http://blog.csdn.net/watermuch/article/details/12320167
Problem Description
Holedox is a small animal which can be considered as one point. It lives in a straight pipe whose length is L. Holedox can only move along the pipe. Cakes may appear anywhere in the pipe, from time to time. When Holedox wants to eat cakes, it always goes to the nearest one and eats it. If there are many pieces of cake in different directions Holedox can choose, Holedox will choose one in the direction which is the direction of its last movement. If there are no cakes present, Holedox just stays where it is.
Input
The input consists of several test cases. The first line of the input contains a single integer T (1 <= T <= 10), the number of test cases, followed by the input data for each test case.The first line of each case contains two integers L,n(1<=L,n<=100000), representing the length of the pipe, and the number of events.
The next n lines, each line describes an event. 0 x(0<=x<=L, x is a integer) represents a piece of cake appears in the x position; 1 represent Holedox wants to eat a cake.
In each case, Holedox always starts off at the position 0.
The next n lines, each line describes an event. 0 x(0<=x<=L, x is a integer) represents a piece of cake appears in the x position; 1 represent Holedox wants to eat a cake.
In each case, Holedox always starts off at the position 0.
Output
Output the total distance Holedox will move. Holedox don’t need to return to the position 0.
Sample Input
3 10 8 0 1 0 5 1 0 2 0 0 1 1 1 10 7 0 1 0 5 1 0 2 0 0 1 1 10 8 0 1 0 1 0 5 1 0 2 0 0 1 1
Sample Output
Case 1: 9 Case 2: 4
题意:一个小孩吃蛋糕,他的起始点在0位置,现在有m个操作,0 x 代表在x位置出现一个蛋糕; 1 代表小孩要去离他最近的点吃蛋糕,如果与他距离最小的点有两个,则去与他上次走的方向相同的点; 如果没有蛋糕,他就不动。 询问m次操作过后,小孩走了多少距离。
X. TreeSet
https://blog.csdn.net/lwt36/article/details/48446687
分析: 没啥就是一个multiset或者map模拟的水题 有个trick就是 - - 有可能会被呆在原地的多个吃的 隐藏的修改了方向然后就跪了
数据还是水了 题目里的数据只有一个方向的 如果不判断等于pre也可以过 但是等于nxt的数据是有的
结论: 论仔细思考 然后写出优美代码的重要性!
while(t--) {
scanf("%d%d", &n, &q);
multiset<int> s;
multiset<int>::iterator iter;
int cur = 0, dir = 1;
long long ans = 0;
while(q--) {
// printf("q: %d\n", q);
int op, x; scanf("%d", &op);
if(op == 0) {
scanf("%d", &x);
s.insert(x);
} else {
if(s.size() == 0) continue;
iter = s.lower_bound(cur);
if(iter == s.end()) x = *(--iter);
else if(iter == s.begin()) x = *iter;
else {
int r = *iter, l = *(--iter);
if(cur - l > r - cur) x = r;
else if(cur - l < r - cur) x = l;
else x = dir ? r : l;
}
if(x > cur) dir = 1;
else if(x < cur) dir = 0;
ans += abs(x - cur);
cur = x;
s.erase(s.find(x));
}
}
printf("Case %d: %I64d\n", ++kase, ans);
}
X.
每次保证到达距离当前最近的位置,若两边相同则选择方向不变的那个方向
由于数据较多,不可以直接选择,那样时间复杂度较高O(N*N),可以用优先级队列降低时间复杂度,当前位置左边的用从大到小排列,右边的从小到大排列,每次移动前比较两个方向的移动距离,左边的肯定与最大的比较,左边的肯定与最小的就比较,于是有了优先级队列的思想,左边大顶堆,右边的小顶堆,总的时间复杂度为O(n*log),应该可以接受。
https://cloverii.github.io/2015-09-14/hdu-4302-holedox-eating/
不过说实话,用两个pq维护虽然听上去很机智,但是代码比较丑 也可能是我写的不好看吧 - - 感觉 multiset 或者 map 写会更简单
优先队列做法:用两个优先队列维护信息,蛋糕大于他当前所在位置的,用从小到大的优先级;蛋糕小于等于他当前所在位置,用从大到小的优先级。两个队列取出的元素再进行比较
- struct cmp
- {
- bool operator () (int x,int y)
- {
- return x > y;
- }
- };
- priority_queue<int , vector<int>, cmp>q; //小到大
- priority_queue<int>qq; //大到小
- int l,n;
- int tt,pos,x=0;
- int sum=0,index = 1;
- void Forword() //向前走
- {
- int tmp = q.top();
- q.pop();
- sum += tmp - x;
- index = 1;
- x = tmp;
- }
- void Back() //向后走
- {
- int tmp = qq.top();
- qq.pop();
- sum += x - tmp;
- index = 0;
- x = tmp;
- }
- int main()
- {
- int t,Case=1;
- cin >> t;
- while(t--)
- {
- while(!q.empty())
- q.pop();
- while(!qq.empty())
- qq.pop();
- scanf("%d%d",&l,&n);
- x=0;
- sum=0;
- index = 1;
- for(int i=0; i<n; i++)
- {
- scanf("%d",&tt);
- if(!tt)
- {
- cin >> pos;
- if(pos <= x)
- qq.push(pos);
- else
- q.push(pos);
- }
- else
- {
- if(q.empty() && qq.empty())
- continue;
- if(q.empty() && ! qq.empty())
- {
- Back();
- continue;
- }
- if(qq.empty() && !q.empty())
- {
- Forword();
- continue;
- }
- if(!q.empty() && !qq.empty())
- {
- int tmp1 = q.top();
- int tmp2 = qq.top();
- if(tmp1 - x < x - tmp2 )
- {
- Forword();
- }
- else if(tmp1 - x > x - tmp2)
- {
- Back();
- }
- else
- {
- if(!index)
- Back();
- else
- Forword();
- }
- }
- }
- }
- printf("Case %d: %d\n",Case++,sum);
- }
- return 0;
- }
X.
每一次,用二分判断,他右边离他最近的蛋糕点,和左边离他最近的蛋糕点。如何判断呢,只需用树状数组维护蛋糕总数,判断小孩当前位置pos 与 mid之间的蛋糕数是否大于等1,是的话可以尝试逼近,否则就远离。
需要注意线段是1 --- L ,而小孩起始点在0,所以有L + 1 个点。
- int n,m;
- int c[MAX];
- int lowbit(int x) {
- return x & (-x);
- }
- void update(int x,int va) {
- while(x < n + 2) {
- c[x] += va;
- x += lowbit(x);
- }
- }
- int query(int x) {
- int sum = 0;
- while(x > 0) {
- sum += c[x];
- x -= lowbit(x);
- }
- return sum;
- }
- int judge(int p,int rp,int lp,int flag) {
- if(rp == -1 && lp == -1) return -1;
- if(rp == -1) return 0;
- if(lp == -1) return 1;
- if(rp - p > p - lp) return 0;
- if(rp - p < p - lp) return 1;
- if(flag == 0) return 0;
- return 1;
- }
- int find(int pos,int l,int r,int kind) {
- int mid , des;
- if(kind == 1) des = INF;
- else des = -1;
- while(l <= r) {
- mid = (l + r) >> 1;
- if(kind == 1) {
- if(query(mid) - query(pos - 1) >= 1) {
- des = min(des,mid);
- r = mid - 1;
- } else l = mid + 1;
- } else {
- if(query(pos) - query(mid - 1) >= 1) {
- des = max(des,mid);
- l = mid + 1;
- } else r = mid - 1;
- }
- // cout << l << ' ' << mid << ' ' << r << endl;
- }
- if(des == INF) return -1;
- return des;
- }
- int main() {
- int T;
- cin >> T;
- int ca = 1;
- while(T--) {
- scanf("%d%d",&n,&m);
- memset(c,0,sizeof(c));
- int a,b;
- int pos = 1, flag = 1; //flag 1:right 0: left
- __int64 dis = 0;
- for(int i=0; i<m; i++) {
- scanf("%d",&a);
- if(a == 1) {
- int rpos = find(pos,pos,n+1,1);
- int lpos = find(pos,1,pos,0);
- int dir = judge(pos,rpos,lpos,flag);
- if(dir == -1) continue;
- if(dir == 1) {
- dis += rpos - pos;
- pos = rpos;
- flag = 1;
- } else {
- dis += pos - lpos;
- pos = lpos;
- flag = 0;
- }
- update(pos,-1);
- } else {
- scanf("%d",&b);
- update(b+1,1);
- }
- }
- printf("Case %d: %d\n",ca ++,dis);
- }
- return 0;
- }