NOIP2002提高组题解

\(D1T1\) 均分纸牌 \((OK)\)

\(D1T2\) 字串变换 \((OK)\)

\(D1T3\) *落体 \((OK)\)

\(D1T4\) 矩形覆盖 \((OK)\)

这年的题其实题目很好,但是数据点又少还水.

\(T1\)贪心模拟入门题.最终每一堆纸牌的数量是已知的,即\(ave=\frac{sum}{n}\).那么把每一堆纸牌数量减去\(ave\)后,如果为负数则要从别人那里拿纸牌,如果为正数就要给别人纸牌,如果为\(0\)就不要动.按照这样从左往右模拟即可.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=105;
int a[N];
int main(){
    int n=read(),sum=0,ans=0;
    for(int i=1;i<=n;++i)a[i]=read(),sum+=a[i];
    int ave=sum/n;for(int i=1;i<=n;++i)a[i]-=ave;
    for(int i=1;i<n;++i){
        if(a[i]!=0)++ans,a[i+1]+=a[i];
    }
    printf("%d\n",ans);
    return 0;
}

\(T2\)字符串做到崩溃.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
int num=1;
string st,ed,turna[10],turnb[10];
map<string,int>Map;queue<string>q;
inline bool bfs(){
    Map[st]=1;q.push(st);
    while(q.size()){
        string u=q.front();q.pop();
        for(int i=1;i<=num;++i){
            string now=u;
            while(1){
                int pos=now.find(turna[i]);
                if(pos==-1)break;
                string v=u;now[pos]='|';
                v.replace(pos,turna[i].size(),turnb[i]);
                if(Map[v])continue;
                Map[v]=Map[u]+1;
                if(v==ed)return true;
                if(Map[v]>=11)return false;
                q.push(v);
            }
        }
    }
    return false;
}
int main(){
    cin>>st>>ed;while(cin>>turna[num]>>turnb[num])num++;num--;
    int pd=bfs();if(pd)printf("%d\n",Map[ed]-1);else puts("NO ANSWER!");
    return 0;
}

\(T3\)蒟蒻只会中规中矩的一个一个点枚举,然后判断能否被接到.反正应该是数据水的原因,只错了一个点,然后就不会做了.然后题解里面全都是\(O(1)\)的神仙做法,发现反而更好理解:就是说每个小球都是同时下落,同时落地的,那么设小球落到车顶\(h-k\)的时间为\(tmin\),落到地上\(h\)的时间为\(tmax\),那么根据小车的速度和车长,可以把小车看做是有这么一段区间,小球的下落位置的横坐标如果在区间内就能被接到.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
double h,s,v,l,k;int n;
int main(){
    scanf("%lf%lf%lf%lf%lf%d",&h,&s,&v,&l,&k,&n);
    double tmin=sqrt((h-k)/5),tmax=sqrt(h/5);
    int imin=(int)(s-tmax*v),imax=(int)(s-tmin*v+l);
    printf("%d\n",min(imax,n)-max(0,imin));
    return 0;
}

\(T4\)这道题我本来觉得很难的,然后随便打了个搜索+剪枝就过了.肯定是数据太水了.我自己是这么想的:把所有点按照横坐标从小到大排序后,最优方案中一个矩形覆盖到的肯定是一段连续的点(这个贪心显然是错的,我虽然造不出\(hack\)数据,但我就觉得这个贪心是错的,因为如果有两个点横坐标相距很远,但是纵坐标一样,那么用一个矩形覆盖这两个点产生的贡献是\(0\),很优).

然后就直接搜索枚举每个矩形覆盖哪一段连续的点即可.再加上一个最优性剪枝,跑得飞快.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=51;
int n,k,ans=1<<30;
struct node{int x,y;}a[N];
inline bool cmp(node x,node y){return x.x==y.x?x.y<y.y:x.x<y.x;}
inline void dfs(int now,int cnt,int sum){
    if(sum>=ans)return;
    if(now>n){ans=sum;return;}
    if(cnt>k)return;    
    for(int i=now;i<=n;++i){
        int xmin=1<<30,xmax=0,ymin=1<<30,ymax=0;
        for(int j=now;j<=i;++j){
            xmin=min(xmin,a[j].x);
            xmax=max(xmax,a[j].x);
            ymin=min(ymin,a[j].y);
            ymax=max(ymax,a[j].y);
        }
        dfs(i+1,cnt+1,sum+(xmax-xmin)*(ymax-ymin));
    }
}
int main(){
    n=read();k=read();
    for(int i=1;i<=n;++i)a[i].y=read(),a[i].x=read();   
    sort(a+1,a+n+1,cmp);
    dfs(1,1,0);printf("%d\n",ans);
    return 0;
}
上一篇:[NOIP2002 提高组] 均分纸牌


下一篇:洛谷P1031 [NOIP2002]均分纸牌