您的位置:首页 > 其它

【BZOJ】1293 [SCOI2009]生日礼物 (这题有多种解法)

2017-04-19 20:25 405 查看
题目传送门

这题的解法主要分为三种,接下来我会详细讲解。

一、首先,我们来看最慢的一种方法,但也是代码量最少的一种——堆。

我们用一个小根堆H来存所有的节点的x坐标,然后对每一种彩珠开一个小根堆,每次从H中取最小的一个坐标,然后枚举每一种颜色,若这种颜色的堆顶坐标小于H的堆顶坐标,就把这个堆顶弹掉,然后判断下一个堆顶,直到:1.当前堆顶坐标不小于H的堆顶坐标;2.当前的堆为空。

对于第一种情况,我们就用一个变量mx来存max(当前堆顶坐标-H的堆顶坐标),然后ans=min(mx);对于第二种情况,也就是接下来我们再也不可能有取m种彩珠了,就可以输出ans了。

附上AC代码:

#include <cstdio>
#include <queue>
#include <cctype>
using namespace std;

priority_queue <int,vector<int>,greater<int> > que[61];
int n,m,x,num,mx,ans;

void read(int& a){
static char c=getchar();a=0;int f=1;
while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
while (isdigit(c)) a=a*10+c-'0',c=getchar();
a*=f;return;
}

int main(void){
read(n),read(m);
for (int i=1; i<=m; ++i){
read(num);
for (int j=1; j<=num; ++j) read(x),que[0].push(x),que[i].push(x);
}
ans=0x7fffffff;
while (!que[0].empty()){
int p=que[0].top();que[0].pop();
mx=0;
for (int i=1; i<=m; ++i){
while (!que[i].empty()&&que[i].top()<p) que[i].pop();
if (que[i].empty()) goto Z;
mx=max(mx,que[i].top()-p);
}
ans=min(ans,mx);
}
Z: printf("%d",ans);
return 0;
}二、然后是hzwer学长的思路——其实是类似于上一种解法的思路,只是把堆替换成了链表。(因为题目明确给出对于每一种颜色的坐标是按升序给出的,所以可以用链表)
附上AC代码:

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;

int n,m,x,num,len,wz[1000010],a[1000010],h[61],nt[1000010],ans,mx;

void read(int& a){
static char c=getchar();a=0;int f=1;
while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
while (isdigit(c)) a=a*10+c-'0',c=getchar();
a*=f;return;
}

void add(int x,int y){wz[++len]=y,nt[len]=h[x],h[x]=len,a[len]=y;}

bool cmp(int p,int q){
return p>q;
}

int main(void){
read(n),read(m);
memset(h,-1,sizeof h);
for (int i=1; i<=m; ++i){
read(num);
for (int j=1; j<=num; ++j)
read(x),add(i,x);
}
sort(a+1,a+1+n,cmp),ans=0x7fffffff;
for (int i=1; i<=n; ++i)
if (a[i]!=a[i+1]){
mx=0;
for (int j=1; j<=m; ++j){
while (wz[h[j]]>a[i]){
if (nt[h[j]]==-1) goto Z;
h[j]=nt[h[j]];
}
if (wz[h[j]]) mx=max(mx,a[i]-wz[h[j]]);
}
ans=min(ans,mx);
}
Z: printf("%d",ans);
return 0;
}三、最后,是一种时间复杂度为O(n)的算法,其实也是挺好想的——维护一个队列,进队顺序是每个节点的坐标,若当前队列里有所有的颜色,就统计答案。
附上AC代码:

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;

struct note{
int col,wz;
bool operator < (const note lyf) const {
return wz<lyf.wz;
}
}a[1000010],que[1000010];
int n,m,num,x,len,l,r,ans,sum,col[61],g;

void read(int& a){
static char c=getchar();a=0;int f=1;
while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
while (isdigit(c)) a=a*10+c-'0',c=getchar();
a*=f;return;
}

int main(void){
read(n),read(m);
for (int i=1; i<=m; ++i){
read(num);
for (int j=1; j<=num; ++j)
read(x),a[++len]=(note){i,x};
}
sort(a+1,a+1+n),g=1,que[l=r=1]=a[1],sum=1,col[a[1].col]=1,ans=0x7fffffff;
while (l<=r){
if (sum==m) ans=min(ans,que[r].wz-que[l].wz);
if (sum==m||g==n){
if (!--col[que[l].col]) --sum;
++l;
}
else {
++g;
if (!col[a[g].col]) ++sum;
++col[a[g].col],que[++r]=a[g];
}
}
printf("%d",ans);
return 0;
}写在最后:以后做题目的时候要有想象力,不要思维僵化。
做题心得:又一次感受到O2是个好东西,前两种解法在洛谷上测是会TLE的,在BZOJ上测三种解法都能过,时间复杂度都还能接受。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: