11年上海賽區賽真題 Bombing
該題考查數據離散處理能力,求點的座標的情況。
0 x,去掉橫座標爲x的那一行上的所有點
1 y,去掉縱座標爲y的那一列上的所有點
輸出每次給出的條件去掉的點的個數。
表示這個題弄了很久,一直超時,也很煩躁。後面參考了別人的想法,才覺得,這個題,是一個內涵題。想做出來,要麼STL用的淋漓盡致,要麼有足夠的聰慧,又要敢於暴力。
該題在網絡上常見的解法有兩種,我借鑑了思想,自己寫了。對於STL的解法,代碼短小精悍,各種容器,各種鑲嵌,各種插入刪除,對我這個STL剛入門的菜鳥來說,簡直就是“太神奇了”。STL解法含金量極高,對於普通解法,是1行頂2行,甚至是3行,我稱之爲【高端霸氣上檔次】。而另一種解法,要敢於用內存,敢於暴力,有要有智慧,排序加二分,剛柔並濟。用最淳樸的思想,解決了各種“離散型”數據的問題,我稱之爲【低調奢華有內涵】。
對於STL的解法:【高端霸氣上檔次】
以最易於理解的邏輯解題,用到容器map和multiset(同一個點上可能不止一個據點)。
主要分爲以x爲關鍵字的mapx和以y爲關鍵字的mapy,其中有嵌套的set,具體的數據結構是:
mapx<int,multiset<int> >(兩個>號中間有空格) 【 int型的關鍵字】存儲橫座標,set中存儲所有橫座標爲【int關鍵字】的點的縱座標。
同理可得mapyM<int ,multiset<int> >
查找時,找到相應的關鍵字,提取其set中元素的個數,然後將另一map容器中與這些點對於的信息刪除,並情況該map該關鍵字的set容器即可
對於排序二分的解法:【低調奢華有內涵】
將輸入的座標,按輸入的順序每個座標賦予一個序號。同時存在兩個數組中arrx(以x值爲關鍵字排序檢索)和數組arry中(以y值爲關鍵字排序檢索)。同時有一個數組flag,flag[i]中的值記錄序號爲i的點的訪問情況,訪問過的標爲“true”,未訪問過的標記爲“false”。
對於所有輸入的點的信息,將座標存入相關數據結構中。輸入詢問後,先用二分搜索,找到相關關鍵字數組中第一次出現詢問關鍵字的數組下標。從該下標開始查詢,直到關鍵字不同或數組尾時結束查詢。對於每個查詢,如若該點沒訪問過(flag標記數組),則計數並標記爲訪問。否則,直接跳過。
對於每次詢問,輸出計數變量的值(初始化爲0)即可。
【高端霸氣上檔次】
#include<cstdio>
#include<iostream>
#include<map>
#include<set>
using namespace std;
int main()
{
int n,m;
int x,y;
int ans;
multiset<int>::iterator it; //multiset的迭代器
while(scanf("%d%d",&n,&m)&&n+m)
{
map<int,multiset<int> > mapx,mapy; //X,Y爲關鍵字的容器
while(n--) //n組值
{
scanf("%d%d",&x,&y);
mapx[x].insert(y); //x軸的x爲關鍵字,與其關聯的Y都存儲在其“值”set<int>中,利於查找
mapy[y].insert(x); //y軸的y爲關鍵字,與其關聯的x都存儲在其“值”set<int>中,利於查找
}
while(m--) //對於m組查找數據
{
scanf("%d%d",&x,&y);
if(!x) //查找,以x值爲關鍵字
{
ans=mapx[y].size(); //提取與x軸值爲y的關鍵字有關聯的,尚未處理的點的情況(數量)
for(it=mapx[y].begin();it!=mapx[y].end();it++)//對該x=y的值所對的點進行刪除
mapy[*it].erase(y); //刪除該點在y關鍵字容器中的相關值
mapx[y].clear(); //清空x=y的點的數據(炸掉了)
}
else //查找,以y值爲關鍵字(同上)
{
ans=mapy[y].size();
for(it=mapy[y].begin();it!=mapy[y].end();it++)
mapx[*it].erase(y);
mapy[y].clear();
}
printf("%d\n",ans); //輸出結果
}
printf("\n"); //每組測試數據後面輸出一個空行
}
return 0;
}
【低調奢華有內涵】
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int x;
int y;
int f;
}arrx[100002],arry[100002]; //每個點的信息,座標及其序號(輸入時的序號)
bool flag[100002]; //記錄相關序號的點是否被訪問過,訪問過標爲“true”,否則標爲“false”
int n,m;
bool cmpx(node a,node b) //按x從小到大排序
{
return a.x<b.x;
}
bool cmpy(node a,node b) //按y從小到大排序
{
return a.y<b.y;
}
int findx(int x) //二分法查找第一個x與給定x相等的座標的結構體數組下標
{
int l=0,r=n-1,mi;
while(l<r)
{
mi=(l+r)/2;
if(arrx[mi].x>=x)r=mi;
else l=mi+1;
}
return l;
}
int findy(int y) //二分法查找第一個y與給定y相等的座標的結構體數組下標
{
int l=0,r=n,mi;
while(l<r)
{
mi=(l+r)/2;
if(arry[mi].y>=y)r=mi;
else l=mi+1;
}
return l;
}
int main()
{
int x,y;
int i,j;
while(scanf("%d%d",&n,&m)&&n+m)
{
for(i=0;i<n;i++)
{
scanf("%d%d",&x,&y);
arrx[i].x=x,arrx[i].y=y,arrx[i].f=i; //初始化x值查詢數組的信息
arry[i].x=x,arry[i].y=y,arry[i].f=i; //初始化y值查詢數組的信息
flag[i]=false; //初始化訪問標記變量(都未訪問)
}
sort(arrx,arrx+n,cmpx); //x值查詢數組以x爲標準排序,利於二分查找
sort(arry,arry+n,cmpy);
while(m--) //m組詢問
{
int sum=0;
scanf("%d%d",&x,&y);
if(!x) //查詢以x爲基準
{
i=findx(y); //在x值查詢數組中,下標爲i的座標是第一個x=y的點
for(;i<n;i++)
{
if(arrx[i].x!=y)break; //直到座標的x值不等於y或訪問到數組末尾,結束循環
else if(!flag[arrx[i].f]) //該點未訪問
flag[arrx[i].f]=true,sum++; //標記爲訪問並且改變計數變量
}
}
else //同上
{
i=findy(y);
for(;i<n;i++)
{
if(arry[i].y!=y) break;
else if(!flag[arry[i].f])
flag[arry[i].f]=true,sum++;
}
}
printf("%d\n",sum);
}
printf("\n");
}
return 0;
}