Alice and Bob
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1253 Accepted Submission(s): 478
Please pay attention that each card can be used only once and the cards cannot be rotated.
For each case, the first line is a number N which means the number of cards that Alice and Bob have respectively. Each of the following N (N <= 100,000) lines contains two integers h (h <= 1,000,000,000) and w (w <= 1,000,000,000) which means the height and width of Alice's card, then the following N lines means that of Bob's.
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <set> #include <queue> using namespace std; int n; struct Card { int person; int height; int width; bool operator < (const Card &t) const { if(height!=t.height) return height<t.height; if(width!=t.width) return width<t.width; return person>t.person; } }; std::set<Card>v; std::priority_queue<Card>s; void data_in() { scanf("%d",&n); Card card; v.clear(); for(int i=0;i<2;i++) { for(int j=0;j<n;j++) { int tmp1,tmp2; scanf("%d %d",&tmp1,&tmp2); card.person=i,card.height=tmp1,card.width=tmp2; v.insert(card); } } } int main() { int t; scanf("%d",&t); while(t--) { data_in(); int cnt=0; std::set<Card>::iterator it; while(!s.empty()) s.pop(); for(it=v.begin();it!=v.end();it++) { // cout<<(*it).person<<" "<<(*it).height<<" "<<(*it).width<<endl; if((*it).person==0) { if(!s.empty()) { cnt++; s.pop(); } } else s.push((*it)); // cout<<cnt<<endl; } printf("%d\n",cnt); } return 0; }
这个代码在杭电oj用c++提交跑了1500ms整。当时我以为已经接近ac了,后来就一直调试,但是结局总是不尽如人意。我先声明,之前我并没有看解题报告。信不信由你,哈哈,娱乐一下嘛,毕竟这个题各种思路我想了一天。我先用set来保存了alice跟bob的卡片的高度和宽度,并重载<号。之后扫描set用一个优先队列来维护之前没有用到过的bob的卡片,如果遇到一个alice的卡片就对优先队列来处理,但是很可惜里面有一个地方没处理对。是因为后来学长给了我一组数据:
1
2
2 7
3 5
1 4
2 7
按照我的代码用是用alice的2 7 去coverbob的1 4,剩下就不能cover了,但这明显是错的。关键就是在这个地方优先队列的优先级出问题了。之后改过之后,发现会有多个bob的卡片可以被alice的某一张卡片所覆盖,但是这时又该如何抉择呢,发现还是不能解决问题,当然就引出了下面的代码,用到了stl的multiset,里面自带的upper_bound函数。
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <vector> #include <queue> #include <set> using namespace std; int n; struct Card { int person; int height; int width; bool operator < (const Card &t) const { if(height!=t.height) return height<t.height; if(width!=t.width) return width<t.width; return person>t.person; } }; std::vector<Card>v; std::multiset<int>s; void data_in() { scanf("%d",&n); Card card; v.clear(); for(int i=0;i<2;i++) { for(int j=0;j<n;j++) { int tmp1,tmp2; scanf("%d %d",&tmp1,&tmp2); card.person=i,card.height=tmp1,card.width=tmp2; v.push_back(card); } } } int main() { int t; scanf("%d",&t); while(t--) { data_in(); sort(v.begin(),v.end()); int cnt=0; std::vector<Card>::iterator it; s.clear(); for(it=v.begin();it!=v.end();it++) { if((*it).person==0) { if(it==v.begin()) continue; else { if(!s.empty()) { std::multiset<int>::iterator itt; itt=s.upper_bound((*it).width); if(itt!=s.begin()) { itt--; // printf("%d\n",(*itt)); // system("pause"); cnt++; s.erase(itt); } else continue; } else continue; } } else s.insert((*it).width); } printf("%d\n",cnt); } return 0; }
代码我就不过多解释了。仔细想一下很容易明白。之后我想到set内部是一棵自建的红黑树。我想那个upper_bound到底怎么运作的呢?当然是先想到的这个思路:遇到bob的卡片先插入,遇到alice的卡片就把数组里面的bob的卡片按照升序排列,然后二分这个数组寻找上界。下面就是这个思路的代码,但是是tle的:
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <vector> #include <queue> #include <set> #define inf 0x7fffffff using namespace std; int n; struct Card { int person; int height; int width; bool operator < (const Card &t) const { if(height!=t.height) return height<t.height; if(width!=t.width) return width<t.width; return person>t.person; } }; std::vector<Card>v; std::multiset<int>s; void data_in() { scanf("%d",&n); Card card; v.clear(); for(int i=0;i<2;i++) { for(int j=0;j<n;j++) { int tmp1,tmp2; scanf("%d %d",&tmp1,&tmp2); card.person=i,card.height=tmp1,card.width=tmp2; v.push_back(card); } } } int main() { int t; scanf("%d",&t); while(t--) { data_in(); sort(v.begin(),v.end()); int cnt=0; std::vector<Card>::iterator it; s.clear(); int us[100000]; int ustart = 1,l,r,mid; for(it=v.begin();it!=v.end();it++) { if((*it).person==0) { if(it==v.begin()) continue; else { sort(us+1,us+ustart+1); if(ustart) { l=1; r=ustart; while(l<r) { mid=(l+r)>>1; if(us[mid]<=(*it).width) l=mid+1; else r=mid; } if(mid&&us[mid]!=inf) { cnt++; us[mid]=inf; } } // if(!s.empty()) // { // std::multiset<int>::iterator itt; // itt=s.upper_bound((*it).width); // if(itt!=s.begin()) // { // itt--; // cnt++; // s.erase(itt); // } // else continue; // } // else continue; } } else //s.insert((*it).width); us[ustart++] = (*it).width; } printf("%d\n",cnt); } return 0; }
肯定是sort超时了,没细想什么时间复杂度,拓展自己思路嘛。后来想怎么搞这个sort呢。我想到在插入的时候可以O(n)处理一下,就是由后往前扫,扫到一个比他小的,就把他插在其后面。这样在遇到alice的卡片之后就不会花费nlogn去排序了,但是插入之后涉及到数据的移动,这样一来估计连nlogn都比不上,呵呵,脑残了一下。之后学长说用什么拉链的方法,那个方法我现在也不知道,但是试着写了写,发现也蛮好写的,下面是这个思路的代码,但是是残的,不会有正确结果,估计也编译不过吧,问题出在了用到过的卡片我会把把的width弄成inf之后二分的时候遇到这些值处理不了了。所以这个思路就放弃了。但我还是把代码弄上来:
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <vector> #include <queue> #include <set> #define inf 1<<17 #define iinf 0x7fffffff using namespace std; int n; struct Card { int person; int height; int width; bool operator < (const Card &t) const { if(height!=t.height) return height<t.height; if(width!=t.width) return width<t.width; return person>t.person; } }; struct node { node *next; int data; int count; node() { next=NULL; data=0; count=0; } }us[100010]; std::vector<Card>v; std::multiset<int>s; void data_in() { scanf("%d",&n); Card card; v.clear(); for(int i=0;i<2;i++) { for(int j=0;j<n;j++) { int tmp1,tmp2; scanf("%d %d",&tmp1,&tmp2); card.person=i,card.height=tmp1,card.width=tmp2; v.push_back(card); } } } int main() { int t; scanf("%d",&t); while(t--) { data_in(); sort(v.begin(),v.end()); int cnt=0; std::vector<Card>::iterator it; s.clear(); int ustart = 1,l,r,mid,mmid; for(it=v.begin();it!=v.end();it++) { cout<<(*it).height<<" "<<(*it).width<<" "<<(*it).person<<endl; if((*it).person==0) { if(it==v.begin()) continue; else { // sort(us+1,us+ustart+1); if(ustart) { l=1; r=ustart; while(l<r) { mid=(l+r)>>1; if(us[mid].data<=(*it).width) l=mid+1; else r=mid; } // cout<<mid<<" "<<us[mid].data<<endl; if(mid&&us[mid].data!=inf) { // cout<<"yes"<<endl; // cout<<us[mid].count<<endl; if(us[mid].count==1) { us[mid].data=inf; cnt++; // cout<<"yes"<<endl; } else { l=1,r=us[mid].count; while(l<r) { mmid=(l+r)>>1; if(us[mid].next[mmid].data<=(*it).width) l=mmid+1; else r=mmid; } if(mmid&&us[mid].next[mmid].data!=inf) { us[mid].next[mmid].data=inf; cnt++; } } } } // if(!s.empty()) // { // std::multiset<int>::iterator itt; // itt=s.upper_bound((*it).width); // if(itt!=s.begin()) // { // itt--; // cnt++; // s.erase(itt); // } // else continue; // } // else continue; } } else //s.insert((*it).width); // us[ustart++] = (*it).width; { if(ustart==1) { us[ustart].next=NULL; us[ustart].count=1; us[ustart++].data=(*it).width; } else { l=1,r=ustart; while(l<r) { mid=(l+r)>>1; if(us[mid].data<=(*it).width) l=mid+1; else r=mid; } if(mid&&mid==ustart-1) { if(us[ustart].data==inf||us) us[ustart].next=NULL; us[ustart].count=1; us[ustart++].data=(*it).width; // cout<<"yes"<<endl; } else { // cout<<"yes"<<endl; us[mid].next[us[mid].count++].data=(*it).width; } } // cout<<ustart<<endl; } } printf("%d\n",cnt); } return 0; }
这个代码比较狗屎。就当给大家提供个思路。后来我想到了线段树的思想,建一棵树来搞,具体怎么搞,还不是特别明朗,当然堆这方面似乎也是可以想想的。
总之感觉今天过的还好吧。多想想总是好的。谢谢。欢迎大家转载,留言。