BestCoder Round #2 解题报告

来源:互联网 发布:淘宝lolcdk是真的吗 编辑:程序博客网 时间:2024/05/20 22:00

最近感觉暑假在家好颓,效率直线下降,码代码速度慢,想题想不出。这次R2的题目也是过了很久才做完……但愿8月份到了学校情况会好些吧。


T1:TIANKENG’s restaurant

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=526&pid=1001

题目大意:给出n(n<=10000)个时间段(形式为hh:mm~hh:mm)和数字x,表示这个时间段会有x个客人来饭店就餐。每个客人需要一张椅子,问最少需要多少张椅子(一个客人离开后椅子可以立马给另一个客人坐)。不超过100组数据,保证时间段不会跨过凌晨0点。

题目分析:这题本质上就是区间加x,最后每一个数取个max。我们在开始时刻+1,在结束时刻后一分钟-1,然后从左往右扫一遍就可以求出答案。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1450;int temp[maxn];int T,n;int Read(){    int x=0;    char c=getchar();    while ( c<'0' || '9'<c ) c=getchar();    while ( '0'<=c && c<='9' ) x=(x<<3)+(x<<1)+(c-'0'),c=getchar();    return x;}int Get(int x,int y){    return 60*x+y;}int main(){    freopen("1.in","r",stdin);    freopen("1.out","w",stdout);    scanf("%d",&T);    while (T--)    {        memset(temp,0,sizeof(temp));        scanf("%d",&n);        for (int i=1; i<=n; i++)        {            int x;            scanf("%d",&x);            int h=Read(),m=Read();            temp[ Get(h,m) ]+=x;            h=Read(),m=Read();            temp[ Get(h,m) ]-=x;        }        int ans=0,now=0;        for (int i=0; i<maxn; i++)            now+=temp[i],ans=max(now,ans);        printf("%d\n",ans);    }    return 0;}

T2:TIANKENG’s rice shop

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=526

题目大意:餐馆提供n(n<=1000)种食物。现在有m(m<=1000)个客人在hh:mm时刻来餐馆排队,每个人会点某一种食物num(num<=10)份。厨师会按照顺序给他们做饭,他会用t(t<=10)分钟做好k(k<=5)份相同类型的食物,如果供应完当前客人之后还有多余的几份,他就会将剩下这些分给已经来排队的要点该类型食物的人(靠前优先),如果还有多余的则扔掉。假设每个客人在拿到num份食物后就会离开,求每个客人的离开时刻。客人抵达的时间不会跨过凌晨0点,不超过100组数据。

题目分析:这题就是个恶心的硬模拟。我们直接开一个n*m的数组维护一下每一种食物有哪些客人点即可,时间O(Tm),其中T是数据组数。这里还有一个很坑的地方:虽然客人抵达的时间顶多是当天23:59,但是他离开的时间可能是好几天后。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1010;int id[maxn][maxn];int head[maxn];int tail[maxn];int Type[maxn];int Time[maxn];int num[maxn];int ans[maxn];int T,n,t,k,m;int Read(){    int x=0;    char c=getchar();    while ( c<'0' || '9'<c ) c=getchar();    while ( '0'<=c && c<='9' ) x=(x<<3)+(x<<1)+(c-'0'),c=getchar();    return x;}int Get(int x,int y){    return 60*x+y;}void Print(int x){    int hh=x/60;    hh%=24;    int mm=x%60;    if (hh<10) printf("0");    printf("%d:",hh);    if (mm<10) printf("0");    printf("%d\n",mm);}int main(){    freopen("2.in","r",stdin);    freopen("2.out","w",stdout);    scanf("%d",&T);    while (T--)    {        scanf("%d%d%d%d",&n,&t,&k,&m);        for (int i=1; i<=n; i++) head[i]=0,tail[i]=0;        for (int i=1; i<=m; i++)        {            int hh=Read(),mm=Read(),y;            scanf("%d%d",&Type[i],&y);            Time[i]=Get(hh,mm);            num[i]=y;            id[ Type[i] ][ ++tail[ Type[i] ] ]=i;        }        int nt=0;        for (int i=1; i<=m; i++) if (num[i])        {            nt=max(nt,Time[i]);            int x=Type[i];            while (num[i]>k) num[i]-=k,nt+=t;            nt+=t;            int Left=k;            while ( head[x]<tail[x] && Time[ id[x][ head[x]+1 ] ]<=nt-t )            {                head[x]++;                int y=id[x][ head[x] ];                if (num[y]<=Left)                {                    Left-=num[y];                    num[y]=0;                    ans[y]=nt;                }                else                {                    num[y]-=Left;                    head[x]--;                    break;                }            }        }        for (int i=1; i<=m; i++) Print(ans[i]);        if (T) printf("\n");    }    return 0;}

T3:TIANKENG’s travel

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=526&pid=1003

题目大意:在一个平面图上给出起点,终点,以及n(n<=1000)个加油站。从一个点可以行驶到另一个和它欧几里得距离小于等于L的点,而且必须直线行驶。就是说如果两点的连线之间还有加油站,那么肯定行驶时必定会经过这个加油站。现在要求最小化从起点到终点所经过的加油站个数,不超过30组数据。

题目分析:这题我一开始没看见必须直线行驶这个条件,以为是n2暴力连边然后BFS,但我又觉得30组数据可能会超(3107)。于是我不停地想nlog(n)的做法,结果根本不会。后来我看了题解才发现要用n2log(n)判断三点共线……
由于两个点中间有加油站时不能直接连边,所以我们先将点按x坐标从小到大排序,相同则按y从小到大。这样点i连向点j的边(i<j)的极角就是(π2,π2],然后我们对一个点i的所有射出的边按极角大小排序,然后相同角度的只连该射线上的第一个点即可。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1010;typedef long long LL;struct edge{    int obj;    edge *Next;} e[maxn*maxn];edge *head[maxn];int cur;int level[maxn];int que[maxn];int he,ta;struct data{    int dx,dy,id;} line[maxn][maxn];struct point{    int X,Y,T;} gas[maxn];int G,n,l;bool Comp1(point x,point y){    return x.X<y.X || ( x.X==y.X && x.Y<y.Y );}bool Comp2(data x,data y){    LL temp1=x.dy;    temp1*=y.dx;    LL temp2=y.dy;    temp2*=x.dx;    bool flag=( x.dx<y.dx || ( x.dx==y.dx && x.dy<y.dy ) );    return temp1<temp2 || ( temp1==temp2 && flag );}bool Judge(int x,int y){    LL temp1=gas[x].X-gas[y].X;    temp1*=temp1;    LL temp2=gas[x].Y-gas[y].Y;    temp2*=temp2;    temp1+=temp2;    temp2=l;    temp2*=temp2;    return temp1<=temp2; }void Add(int x,int y){    cur++;    e[cur].obj=y;    e[cur].Next=head[x];    head[x]=e+cur;}int Bfs(){    int s,t;    for (int i=0; i<=n; i++)    {        level[i]=0;        if (!gas[i].T) s=i;        if (gas[i].T==n) t=i;    }    he=0,ta=1,que[1]=s;    while (he<ta)    {        int node=que[++he];        for (edge *p=head[node]; p; p=p->Next)        {            int son=p->obj;            if ( son && !level[son] )            {                level[son]=level[node]+1;                que[++ta]=son;            }        }    }    return level[t];}int main(){    freopen("3.in","r",stdin);    freopen("3.out","w",stdout);    scanf("%d",&G);    while (G--)    {        scanf("%d%d",&n,&l);        n++;        scanf("%d%d",&gas[0].X,&gas[0].Y);        scanf("%d%d",&gas[n].X,&gas[n].Y);        for (int i=1; i<n; i++) scanf("%d%d",&gas[i].X,&gas[i].Y);        for (int i=0; i<=n; i++) gas[i].T=i;         sort(gas,gas+n+1,Comp1);        cur=-1;        for (int i=0; i<=n; i++) head[i]=NULL;        for (int i=0; i<n; i++)        {            for (int j=i+1; j<=n; j++)            {                line[i][j-i].id=j;                line[i][j-i].dx=gas[j].X-gas[i].X;                line[i][j-i].dy=gas[j].Y-gas[i].Y;            }            sort(line[i]+1,line[i]+n-i+1,Comp2);            if ( Judge(i,line[i][1].id) )                Add(i,line[i][1].id),Add(line[i][1].id,i);            for (int j=2; j<=n-i; j++)            {                LL temp1=line[i][j].dy;                temp1*=line[i][j-1].dx;                LL temp2=line[i][j-1].dy;                temp2*=line[i][j].dx;                if ( temp1!=temp2 && Judge(i,line[i][j].id) )                    Add(i,line[i][j].id),Add(line[i][j].id,i);            }        }        int ans=Bfs();        if (ans) printf("%d\n",ans-1);        else printf("impossible\n");    }    return 0;}

T4:TIANKENG’s restaurant(Ⅱ)

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=526&pid=1004

题目大意:给出一个只包含A~H,长度不超过106的字符串,输出一个最短的只包含A~H的答案串,使得答案串不是给定串的子串。如果有多组解,则输出字典序最小的那个。

题目分析:这题空间只有64M我还能说什么……当初一看这题很高兴因为这就是个SAM的裸题呀。我们把后缀自动机构出来然后就是要从根节点最快地转移到NULL并且保证字典序最小,结果空间炸得不要不要的。我伤心无奈之下忽然想起陈老师PPT里说SAM的边数不会超过状态数+字符串长度,于是我就把son[8]的指针数组改成了3*len的邻接链表(本来想写treap的,但考虑到空间开销……),时间多了个常数8而已。然而还是炸空间,后来看题解知道这就是个Hash?!
我们首先想答案会有多长。如果答案长度是7,那么长度为7的串就有87=2097152个,而原串中长度为7的子串顶多只有10000007+1=999994个,于是答案长度肯定不超过7。我们不妨枚举长度ans,将字符串长度为ans的子串的hash值用一个下标数组记录,最后遍历一边下标数组即可。时间复杂度O(n)
时候我对拍了一下SAM+邻接链表的程序和Hash的程序,发现没什么问题,只不过前者的常数和空间都比较大而已。极限数据Hash要3s,而SAM+邻接链表要30s……

CODE(Hash):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1000010;const int maxl=8;bool vis[maxn];int val[maxl];int w[maxl];int cur=0;char s[maxn];int t,slen;void Print(int x,int len){    for (int i=1; i<=len; i++) w[++cur]=x&7,x>>=3;    while (cur) printf("%c",'A'+w[cur]),cur--;    printf("\n");}int main(){    freopen("4.in","r",stdin);    freopen("4.out","w",stdout);    val[0]=1;    for (int i=1; i<maxl; i++) val[i]=val[i-1]<<3;    scanf("%d",&t);    while (t--)    {        scanf("%s",&s);        slen=strlen(s);        for (int ans=1; ans<=maxl; ans++)        {            memset(vis,false,sizeof(vis));            int Hash=0;            for (int i=1; i<=ans; i++) Hash=(Hash<<3)|(s[i-1]-'A');            for (int i=1; i<=slen-ans+1; i++)            {                vis[Hash]=true;                Hash-=((s[i-1]-'A')*val[ans-1]);                Hash=(Hash<<3)|(s[i+ans-1]-'A');             }            bool sol=false;            for (int i=0; i<val[ans]; i++)                if (!vis[i])                {                    Print(i,ans);                    sol=true;                    break;                }            if (sol) break;        }    }    return 0;}

CODE(SAM+邻接链表):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1000010;const int maxc=8;struct State;struct edge{    int son;     State *obj;    edge *Next;} e[maxn*3];int num;struct State{    int val,id,len;    edge *head;    State *parent;} SAM[maxn<<1];State *Root,*last;int cur;int cnt[maxn];State *Rank[maxn<<1];char s[maxn];int T,slen;State *New_state(int v){    cur++;    SAM[cur].val=v;    SAM[cur].head=NULL;    SAM[cur].parent=NULL;    return SAM+cur; }State *Get(State *P,int x){    for (edge *p=P->head; p; p=p->Next)        if (p->son==x) return p->obj;    return NULL;}void Add(State *P,int x,State *Q){    if (!Q) return;    num++;    e[num].son=x;    e[num].obj=Q;    e[num].Next=P->head;    P->head=e+num;}void Build(int x){    int to=s[x]-'A';    x++;    State *P=last,*NP=New_state(x);    last=NP;    while ( P && !Get(P,to) ) Add(P,to,NP),P=P->parent;    if (!P)    {        NP->parent=Root;        return;    }    State *Q=Get(P,to);    if (P->val+1==Q->val) NP->parent=Q;    else    {        State *NQ=New_state(P->val+1);        for (int i=0; i<maxc; i++) Add(NQ,i, Get(Q,i) );        NQ->parent=Q->parent;        NP->parent=Q->parent=NQ;        while (P)        {            State *tag=Get(P,to);            if (tag!=Q) break;            for (edge *p=P->head; p; p=p->Next)                if (p->son==to)                {                    p->obj=NQ;                    break;                }            P=P->parent;        }    }}int main(){    freopen("4.in","r",stdin);    freopen("4.out","w",stdout);    scanf("%d",&T);    while (T--)    {        scanf("%s",&s);        slen=strlen(s);        num=cur=-1;        last=Root=New_state(0);        for (int i=0; i<slen; i++) Build(i);        for (int i=0; i<=slen; i++) cnt[i]=0;        for (int i=0; i<=cur; i++) cnt[ SAM[i].val ]++;        for (int i=1; i<=slen; i++) cnt[i]+=cnt[i-1];        for (int i=0; i<=cur; i++) Rank[ --cnt[ SAM[i].val ] ]=SAM+i;        for (int i=cur; i>=0; i--)        {            Rank[i]->id=0;            State *tag=Get(Rank[i],0);            Rank[i]->len=(tag? tag->len:0)+1;            for (int j=1; j<maxc; j++)            {                tag=Get(Rank[i],j);                int temp=(tag? tag->len:0)+1;                if (temp<Rank[i]->len) Rank[i]->len=temp,Rank[i]->id=j;            }        }        State *P=Root;        while (P) printf("%c",'A'+P->id),P=Get(P,P->id);        printf("\n");    }    //printf("%d\n",sizeof(e)/1024);    //printf("%d\n",sizeof(SAM)/1024);    return 0;}
原创粉丝点击