【NOIP2016普及组】关于这四道水题

2016普及组已经考完了,我也写了四对,八个各种姿势的“freopen”。
废话不多说:
题目链接在此:NOIP2016普及组
第一题:铅笔(pencil):
初步判断水题,同学说算价格时用for循环,RX:

#include <cstdio>
using namespace std;
int n,a,b,val=99999999;
int main(){
    scanf("%d",&n);
    for (int i=1; i<=3; i++) {
        scanf("%d%d",&a,&b);
        int k=0,s=0;
        for (; k<n; k+=a,s+=b);
        if (s<val) val=s;
    }
    printf("%d",val);
    return 0;
}

然而聪明人都用我的做法:

#include<cstdio>
#include<algorithm>
using namespace std;
float n,w[10];
int val=999999999,v[10];
int main(){
    //freopen("pencil.in","r",stdin);
    //freopen("pencil.out","w",stdout);
    scanf("%f",&n);
    for(int i=1;i<=3;i++){
        scanf("%f%d",&w[i],&v[i]);
        val=min(int(n/w[i]+0.9999)*v[i],val);//精髓之处
    }
    printf("%d",val);
    return 0;
}

第二题:回文日期(date):
也是水题啊:
爆搜都不会超时,也就O(10^8)
从起始日期到结束日期一天一天搜,就行啦,RX:

#include<cstdio>
#include<algorithm>
using namespace std;
int date[2],m[13]={0,31,28,31,30,31,30,31,31,30,31,30,31},ans;
bool R(int r){  return (r%4==0&&r%100!=0)||r%400==0; }
int nextmonth(int r){ return r==12?1:r+1; }
bool round(int r){
    char a[8];
    int i=0;
    while(r){
        a[i++]=r%10;
        r/=10;
    }
    for(int i=0;i<=3;i++)
        if(a[i]!=a[7-i])
            return 0;
    return 1;
}
int nextday(int x){
    int year,mon,day;
    day=x%100;
    mon=x%10000/100;
    year=x/10000;
    if(mon==2&&day==28&&R(year))day++;
    else if(day>=m[mon]) mon=nextmonth(mon),day=1;
    else day++;
    if(day==1&&mon==1) year++;
    return  year*10000+mon*100+day;
}

int main(){
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    for(int i=0;i<=1;i++){
        scanf("%d",&date[i]);
    }
    for(int i=date[0];i<=date[1];i=nextday(i))
        if(round(i))
            ans++;
    printf("%d",ans);
    return 0;
}

另外还可以枚举年,因为一年最多只有一个回文日期,起始年和终止年特殊处理就行了

第三题:海港(port)
这是一道模拟题,我用队列做,但明显超时
我的思路是这样的,来一个人就入队,来一条船就让他们出队,搜答案。
但这样有一个缺点,一天内的人还得重新入队,如果10000条船挤在一天进港,
简直妙不可言。
正解是加一个数组记录岛上每种国籍有多少人,令每个人在岛上待一天就走,
船入港,人入队,别的船入港,就判断别的船的入港时间与人的入港时间相差超不超过一天,如果超过,人就出队,不然,现在岛上的不同国籍数就是答案。
正解RX:

#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
struct node{
    int nation,t;
    node(){}
    node(int a,int b){
        nation=a;
        t=b;
    }
};
queue<node>q;
int n,t,k,c[100005],sum,a;
int main(){
    //freopen("port.in","r",stdin);
    //freopen("port.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&t,&a);
        for(int j=1;j<=a;j++){
            scanf("%d",&k);
            q.push(node(k,t));
            c[k]++;
            if(c[k]==1)
                sum++;
        }
        while(q.front().t<=t-86400){
            c[q.front().nation]--;
            if(!c[q.front().nation]) sum--;
            q.pop();
        }
        printf("%d\n",sum);
    }
}

第四题:魔法阵(magic)
这题就显得高大上了,40000,15000的范围令人不敢恭维,不过,还是暴搜。
50分做法:
就是搜,搜四个数,并判断可不可行,并且先对魔法物品按魔法值排序,再枚举。

#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int m,n,ans[40001][4];
struct node{
    float a;
    int ID;
    bool operator < (const node next)const{
        return a<next.a;
    }
}w[40001];
int main(){
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%f",&w[i].a);
        w[i].ID=i;
    }
    sort(w+1,w+1+m);
    for(int a=1;a<=m-3;a++)
        for(int b=a+1;b<=m-2;b++){
            if(w[b].a==w[a].a) continue;
            for(int c=b+1;c<=m-1;c++){
                if(w[b].a==w[c].a) continue;
                    if(w[c].a>4*w[b].a-3*w[a].a)
                        for(int d=c+1;d<=m;d++){
                            if(w[c].a==w[d].a)continue;
                            if(w[b].a-w[a].a==2*w[d].a-2*w[c].a)
                            {
                                ans[w[a].ID][0]++;
                                ans[w[b].ID][1]++;
                                ans[w[c].ID][2]++;
                                ans[w[d].ID][3]++;
                            }
                        }
            }
        }
    for(int i=1;i<=m;i++){
        for(int j=0;j<3;j++)
            printf("%d ",ans[i][j]);
        printf("%d\n",ans[i][3]);
    }
    return 0;
}

65分做法:

#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int m,n,ans[40001][4];
struct node{
    float a;
    int ID;
    bool operator < (const node next)const{
        return a<next.a;
    }
}w[40001];
int main(){
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%f",&w[i].a);
        w[i].ID=i;
    }
    sort(w+1,w+1+m);
    for(int a=1;a<=m-3;a++)
        for(int b=a+1;b<=m-2;b++){
            if(w[b].a==w[a].a) continue;
            for(int c=b+1;c<=m-1;c++){
                if(w[b].a==w[c].a) continue;
                    if(w[c].a>4*w[b].a-3*w[a].a)
                        for(int d=c+1;d<=m;d++){
                            if(w[c].a==w[d].a)continue;
                            if(w[b].a-w[a].a<2*w[d].a-2*w[c].a) break;//注意这行
                            if(w[b].a-w[a].a==2*w[d].a-2*w[c].a)
                            {
                                ans[w[a].ID][0]++;
                                ans[w[b].ID][1]++;
                                ans[w[c].ID][2]++;
                                ans[w[d].ID][3]++;
                            }
                        }
            }
        }
    for(int i=1;i<=m;i++){
        for(int j=0;j<3;j++)
            printf("%d ",ans[i][j]);
        printf("%d\n",ans[i][3]);
    }
    return 0;
}

其实就是加了个剪枝条件,但效果很好,速度快了接近一半。
85分做法:

#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
char rBuf[1<<25],pBuf[1<<25];
char *rbuf=rBuf,*pbuf=pBuf;
int getint()
{
    int x=0;
    while(*rbuf<'0'||*rbuf>'9')rbuf++;
    while(*rbuf>='0'&&*rbuf<='9')x=x*10+(*rbuf++-'0');
    return x;
}
void putint(int x)
{
    if(x>9)putint(x/10);
    *pbuf++=x%10+'0';
}
const int N=15005,M=40005;
int n,m,x[M],ans[N][4],s[N];
void output()
{
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<4;j++)
        {
            putint(ans[x[i]][j]);
            *pbuf++=' ';
        }
        *pbuf++='\n';
    }
    fwrite(pBuf,pbuf-pBuf,1,stdout);
}
int main()
{
    fread(rBuf,1,1<<25,stdin);
    n=getint();m=getint();
    for(int i=1;i<=m;i++)
        s[x[i]=getint()]++;
    for(int a=1;a<=n;a++)
        if(s[a])
            for(int b=a+1;b<=n;b++)
                if(s[b])
                    for(int c=max(b+1,4*b-3*a+1);c<=n;c++)
                        if(s[c])
                        {
                            if(((b-a+2*c)&1)==0)
                            {
                                int d=(b-a+2*c)>>1;
                                if(d>c&&d<=n&&s[d])
                                {
                                    ans[a][0]+=s[b]*s[c]*s[d];
                                    ans[b][1]+=s[a]*s[c]*s[d];
                                    ans[c][2]+=s[a]*s[b]*s[d];
                                    ans[d][3]+=s[a]*s[b]*s[c];
                                }
                            }
                        }
    output();
    return 0;
}

这个算法是三个for循环,就是根据:
Xb-Xa=2(Xd-Xc);
消掉一个元,枚举余下的3个就行啦。
AC正解!!!:
为什么题目告诉你”每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。”
正解正是利用了这句话,将40000个物品分为15000个重量各不相同的物品。
然后用一个sam[15001]记录每个重量有多少个物品。
另外,我们设
Xd-Xc=i(i<=n/9);
可得Xb-Xa=2i, Xc-Xb>6i;
因此外层循环枚举i,
内层第一个循环枚举B,C的距离J
可得:d[i*3+J]+=w[i*2]*w[i*3]*w[i*2+J]
以此类推,代码RX:

#include<cstdio>
#include<algorithm>
#define L 20000
using namespace std;
int a[L],b[L],c[L],d[L];
int w[40001],sam[15001],n,m;
int main()
{
    //freopen("magic.in","r",stdin);
    //freopen("magic.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&w[i]);
        sam[w[i]]++;
    }
    for(int i=1;i<=n/9;i++){
        int loc_d_least=i*9+1,possiblity_a_b=0;
        for(int j=i*9+2;j<=n;j++){
            possiblity_a_b+=sam[j-loc_d_least]*sam[j-loc_d_least+2*i];
            d[j]+=possiblity_a_b*sam[j-i];
            c[j-i]+=possiblity_a_b*sam[j];
        }
        int possiblity_c_d=0;
        for(int j=n-9*i-1;j>=1;j--){
            possiblity_c_d+=sam[j+loc_d_least]*sam[j+loc_d_least-i];
            a[j]+=possiblity_c_d*sam[j+2*i];
            b[j+2*i]+=possiblity_c_d*sam[j];
        }
    }
    for(int i=1;i<=m;i++)
        printf("%d %d %d %d\n",a[w[i]],b[w[i]],c[w[i]],d[w[i]]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/53304339
今日推荐