NOIP2017模拟赛1

a

奶牛Bessie有N(1≤N≤50)块巧克力,从左往右排成一行,编号从0到N-1。第i块巧克力的颜色是color[i]。我们定义一个参数MaxLen,它表示:具有相同颜色的连续一段巧克力的最大长度。例如:有10块巧克力,颜色分别是: ADDDABBAAB,那么MaxLen=3,因为有3块颜色是D的巧克力,而且这3块巧克力的位置是连续的。为了使得MaxLen最大,Bessie可以交换相邻两块巧克力的位置,但是Bessie 总共交换的次数不能超过给定的值swap(1≤swap≤2500)。那么MaxLen的最大值是多少?

分析

一开始以为是DP(毕竟N那么小),随便想想以后就发现暴力就可以了。枚举每一个字母,不断把左边最近相同的和右边最近相同的取一个最小的,然后随便乱搞就可以了。
随便分析一下正确性:①如果不是选(左边或右边)最近的,那么选择最近的肯定会更优。
②如果左边的是最近的且比右边最近的要小,但却选择了右边最近的和第二近的(如图),那么选择左边最近的和右边最近的不会差
图

程序

#include <fstream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

char ch[110];
int g,n,s,S,le,ri,L,R,ans;

int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&g);
    for (int G=1;G<=g;G++) {
        scanf("%d%d",&n,&s);
        scanf("%s",&ch);
        ans=0;
        for (int i=0;i<n;i++) {
            L=i;R=i;le=i-1;ri=i+1;S=s;
            while (le>=0||ri<n) {
                ans=max(ans,R-L+1);
                if (S-(L-le-1)<0&&S-(ri-R-1)<0) break;
                while (le>=0&&ch[le]==ch[i]&&S-(L-le-1)>=0) {le--;L--;S-=L-le-1;}
                while (ri<n&&ch[ri]==ch[i]&&S-(ri-R-1)>=0) {ri++;R++;S-=ri-R-1;}
                le--;ri++;
            }
            ans=max(ans,R-L+1);
        }
        printf("%d\n",ans);
    }

    return 0;
}

小结

这种题一定要A,要不然等着被老师PI吧。。想问题可以稍微简单一点。
a

b

有K种不同规则的长方体砖块,长宽高分别是:1×1×1、2×1×1、3×1×1…,K×1×1.还给出一个W×1×1的地基,如下图所示,W=9, k=3,下面的是地基:
1
现在你要在地基上堆放砖块,必须满足如下的规则:
1、 砖块只能横放,不能竖放。
2、 砖块必须放置在整数位置,且不能越出地基。
3、 砖块任何部分的正下方都必须要有其他砖块或者是地基。
例如:下图是不合法的放置方式,有4个不合法的地方:
2
我们定义一种堆放砖块方案的高度height:它是指该方案中最高的砖块是第几层,其中地基是第0层,例如(图二)的高度是3。
我们定义不同的堆放方案:例如有两种堆放方案A和B,只要满足两个条件之一,方案A和方案B就是不同的方案:
1、 在某个位置方案A有砖块而方案B在该位置没有砖块,或者方案B有砖块而方案A没有。
2、 在某个位置方案A和方案B都有砖块,但是它们不是同一种规格的砖块。

给定地基的长度W,和地砖的最大长度K,你的任务是计算有多少种不同的堆放砖块的方案,你的堆放砖块方案的高度height不能超过给定的H。答案模1000000007。(1≤W,H≤50,1≤K≤W)
例如:下图是W=3, k=3, H=2的所有合法方案:
3

分析

这题好像一看就是DP,而实际上就是DP。
f[i][j]表示剩下i行,有连续长度为j的地基(可以有些格子不盖积木)让你往上盖,所有的合法方案。
s[i]表示用1×1×1、2×1×1、3×1×1…,K×1×1的积木来盖1层长度为i(所有都要盖)的所有合法方案数。
其中s随便求都可以,问题就是f了。
如果这一段连续的地基从头开始处理,可以分成几种情况。
①第一格不盖积木,f[i][j]+=f[i][j-1]
②从第一格连续盖k个积木,且后面空一格不盖积木,f[i][j]+=f[i][j-k-1]*f[i-1][k]*s[k]
随便算都知道时间复杂度是够用的。

程序

#include <fstream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

const int Mod = 1000000007;
long long f[60][60];
int s[60],G,w,h,p;

int main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);

    scanf("%d",&G);
    for (int g=1;g<=G;g++) {
        scanf("%d%d%d",&w,&h,&p);
        s[0]=1;
        for (int i=1;i<=w;i++){
            s[i]=0;
            for (int j=i-1;j>=0&&i-j<=p;j--)
            s[i]=(s[j]+s[i])%Mod;
        }

        f[1][0]=1;
        for (int i=1;i<=w;i++) {
            f[1][i]=(s[i]+f[1][i-1])%Mod;
            for (int j=1;j<=i-1;j++)
            f[1][i]=(f[1][i-j-1]*s[j]%Mod+f[1][i])%Mod;
        }

        for (int i=2;i<=h;i++) {
            f[i][0]=1;
            for (int j=1;j<=w;j++) {
                f[i][j]=(f[i-1][j]*s[j]%Mod+f[i][j-1])%Mod;
                for (int k=1;k<=j-1;k++)
                f[i][j]=(f[i][j]+f[i-1][k]*s[k]%Mod*f[i][j-k-1]%Mod)%Mod;
            }
        }
        printf("%d\n",f[h][w]);
    }

    return 0;
}

小结

想的时候出现了一点错误,就是没有考虑第一格可以空着,然后盖积木时还枚举了后面连续空了多少个格,然后就×w×了。不过进行了及时的调整就好多了。注意long long和连乘时的mod运算。
b

c

Farmer John 有一个宽是w单位,高是h单位的草莓园。则草莓园被分成w * h格. 突然间,某些格子起火了,Farmer John马上打电话给火警. 每过一个小时,着火的格子的附近格子也会着火 (上下左右和对角线),如下图所示:

你可以看到,刚开始只有两个格子有火,3小时后所有格子都着火了. 如果火警2小时后赶到,那么就可以挽救6个格子.如果火警1小时后赶到, 就可以挽救12个格子, 如果火警即刻赶到(即0小时),那么可以挽救 23 个格子。
现在Farmer John至少要保留 need 个格子,求火警察最迟可以几个小后赶到? (1≤w,h≤10^9,1≤n≤50)

分析

二分答案+离散化+平面扫描,分析很容易。其中平面扫描时n太小,以至于我可以排序后O(N)暴力找。

程序

#include <fstream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

struct node{
    int x,y;
}a[60],t[60];
int ax[60],ay[60],bx[60],by[60],w,h,L,R,mid,he,ta,G,n;
long long ans,s;

bool cmp(node A,node B) {
    return A.x<B.x||A.x==B.x&&A.y<B.y;
}

long long get(int x,int y,int z) {
    long long s=0;
    for (int i=x;i<=y;i++) 
    {t[i].x=ay[i];t[i].y=by[i];}
    sort(t+x,t+y+1,cmp);
    int L=t[x].x,R=t[x].y;x++;
    while (x<=y) {
        if (t[x].x<=R) R=max(R,t[x].y);
        else {
            s+=R-L+1;
            L=t[x].x;R=t[x].y;
        }
        x++;
    }
    s+=R-L+1;
    return s*z;
}

int main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);

    scanf("%d",&G);
    for (int g=1;g<=G;g++) {
        scanf("%d%d%I64d",&w,&h,&s);
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
        sort(a+1,a+1+n,cmp);
        L=0;R=max(w,h)+1;
        while (L+1<R) {
            mid = (L+R)>>1;
            for (int i=1;i<=n;i++){
                ax[i]=max(a[i].x-mid,1);
                ay[i]=max(a[i].y-mid,1);
                bx[i]=min(a[i].x+mid,w);
                by[i]=min(a[i].y+mid,h);
            }
            ax[n+1]=ay[n+1]=bx[n+1]=by[n+1]=0;ans=0;

            he=1;ta=1;
            while (he<=n) {
                while (he<ta&&bx[he]<ax[ta]||ta>n) {
                    while (bx[he+1]==bx[he]) he++;
                    he++;
                    if (he>n||he>=ta) break;
                    if (bx[he]<ax[ta]||ta>n) ans+=get(he,ta-1,bx[he]-bx[he-1]);
                    else ans+=get(he,ta-1,ax[ta]-bx[he-1]-1);
                }
                while (bx[he]>=ax[ta]&&ta<=n) {
                    while (ax[ta]==ax[ta+1]) ta++;
                    ta++;
                    if (bx[he]>=ax[ta]&&ta<=n) ans+=get(he,ta-1,ax[ta]-ax[ta-1]);
                    else ans+=get(he,ta-1,bx[he]-ax[ta-1]+1);
                }
            }

            if ((long long)w*h-ans>=s) L=mid;else R=mid;
        }
        printf("%d\n",L);
    }

    return 0;
}

小结

思考深度不大,但很容易写错。(如果不用排序写真不一定能写对)
作为一题暴力很容易,算法很容易码错(不熟悉),这种题记得要对拍!!
c

总结

AK这套题看上去很简单,但对于我来说时间还是有点紧,说明水平还是不够。
QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值