第一章:模拟、枚举和贪心(7.16、7.17)

枚举

NC16593 [NOIP2011]铺地毯

直接for找到最大的编号即可

前缀和和差分

NC16649 校门外的树

差分数组统计sum,或者也可以用区间覆盖贪心的思想去做

尺取法(双指针)

NC18386 字符串

记录l和r,要求包括所有小写字母,先r右移到满足条件,再l左移,最终取最小的ans

NC207040 丢手绢

距离为顺时针或者逆时针取min

所以离得最远的两个小朋友的距离<=圆周长的一版

考虑圆上的尺取法 i和j的顺时针距离(输入也是给的顺时针)>=周长一半的时候更新判断ans

#include<bits/stdc++.h>
using namespace std;
int a[100003];
int main()
{
    int n;scanf("%d",&n); 
    int all=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        all+=a[i];
    }
    int x=all/2;
    int ans=0;
    int sum=0;
    int l=1;
    for(int i=1;i<=n;++i)
    {
        while(sum<x)
        {
            l%=n;
            if(!l)l=n;
            sum+=a[l];
            l++;
        }
        ans=max(ans,min(sum,all-sum));
        sum-=a[i];
    }
    printf("%d\n",ans);
}
丢手绢

枚举+递推

NC20241 [SCOI2005]扫雷MINE

只有两列的扫雷,设a[i]为第二列i位置的数,f[i]为第一列i位置是否有雷(0/1)

可得a[i]=f[i+1]+f[i]+f[i-1] (a[1]和a[n]特殊判断)

递推得f[i+1],然后分情况枚举

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int a[10003],f[10003];
LL ans=0;
int n;
int work()//a[i]=f[i]+f[i-1]+f[i+1]
{
    for(int i=2;i<n;++i)
    {
        int t=a[i]-f[i]-f[i-1];
        if(t<0||t>2)return 0;
        f[i+1]=t;
    }
    if(f[n]+f[n-1]!=a[n])return 0;
    return 1;
}
int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    if(a[1]>2||a[n]>2){printf("0\n");return 0;}
    if(a[1]==2)
    {
        f[1]=1;f[2]=1;
        ans+=work();
    }
    else if(a[1]==0)
    {
        f[1]=0;f[2]=0;
        ans+=work();
    }
    else if(a[1]==1)
    {
        f[1]=1;f[2]=0;
        ans+=work();
        memset(f,0,sizeof(f));
        f[1]=0;f[2]=1;
        ans+=work();
    }
    printf("%lld\n",ans);
}
扫雷

NC235250 牛可乐的翻转游戏

(待补)

指针优化(预处理数组往后跳)

NC23053 月月查华华的手机

注意是子序列而不是子串(否则直接kmp)

所以处理nextt[i][j]数组,表示在i的右边,j+'a'的第一个位置(相当于贪心去往后跳)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
char A[1000003],b[1000003];
int nextt[1000003][30];
int main()
{
    scanf("%s",A+1);
    int l=strlen(A+1);
    for(int i=l-1;i>=0;--i)
    {
        for(int j=0;j<26;++j)nextt[i][j]=nextt[i+1][j];
        nextt[i][A[i+1]-'a']=i+1;
    }
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%s",b+1);
        int l2=strlen(b+1);
        int k=0,flagg=0;
        for(int i=1;i<=l2;++i)
        {
            if(nextt[k][b[i]-'a'])k=nextt[k][b[i]-'a'];
            else{flagg=1;break;}
        }
        if(flagg)printf("No\n");
        else printf("Yes\n");
    }
}
月月查华华的手机

贪心

一般贪心

NC23036 华华听月月唱歌

区间覆盖问题(注意超过n了break掉,这个没注意到调了好久)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct qj{
    int l,r;
}w[100003];
bool cmp(qj a,qj b)
{
    if(a.l==b.l)return a.r>b.r;
    return a.l<b.l;
}
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)scanf("%d%d",&w[i].l,&w[i].r);
    sort(w+1,w+1+m,cmp);
    int l=w[1].l,r=w[1].r;
    w[m+1].l=0x3f3f3f3f;
    if(l!=1){printf("-1\n");return 0;}
    int ans=1;
    for(int i=2;i<=m;++i)
    {
        if(w[i].l>=l&&w[i].r<=r)continue;
        if(w[i].l>r+1){printf("-1\n");return 0;}
        int R=r;
        while(w[i].l<=r+1&&i<=m)
        {
            R=max(R,w[i].r);
            i++;
        }
        i--;
        if(R>r)r=R,ans++;
        if(r>=n)break;
    }
    if(r<n)printf("-1\n");
    else printf("%d\n",ans);
}
华华听月月唱歌

推公式+贪心

NC16561 国王的游戏

以下是牛客的题解:

我们对于国王身后的两个点来分析

队列可能是这样的:

*LeftRight
king:   a0   b0
p1   a1   b1
p2   a2   b2

那么我们计算可得ans1=max(a0/b1,a0×a1/b2)

队列也有可能是这样的

*LeftRight
king:   a0   b0
p2   a2   b2
p1   a1   b1

那么我们计算可得ans2=max(a0/b2,a0×a2/b1)

我们来对比一下两个答案:

ans1=max(a0/b1,a0×a1/b2)

ans2=max(a0​/b2,a0×a2/b1)

可以替换得:

ans1=max(k1,k2)

ans2=max(k3,k4)

显然我们可以得到:

a0×a1​/b2>a0/b2

a0×a2/b1​>a0/b1

即:k2>k3​   k4>k1

如果ans1<ans2

那么易得:

k4>k2

即: a0×a2/b1>a0×a1/b2

变形可得:

a1×b1<a2×b2

a1×b1<a2×b2时,我们也能够得到ans1<ans2的结论

所以,为了ans取到最小值,我们需要将ai×bi较小的放在前面

那么我们以ai×bi为关键字排序即可

同时要写高精度

按位贪心

NC18979 毒瘤xor

对于每一位去看所有的数,如果为1的数多于为0的数,此位取0,否则取1

所以问题转化为统计每一位上有多少个数是0,多少个数是1

每次询问区间,考虑前缀和进行处理

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int sum[100003][35];
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        int a;scanf("%d",&a);
        for(int j=0;j<31;++j)
        {
            if(a&1)sum[i][j]=sum[i-1][j]+1;
            else sum[i][j]=sum[i-1][j];
            a>>=1;
        }
    }
    int Q;scanf("%d",&Q);
    while(Q--)
    {
        int l,r;scanf("%d%d",&l,&r);
        int x=0,all=r-l+1;
        for(int i=0;i<31;++i)
        {
            int k=sum[r][i]-sum[l-1][i];
            if(k<all-k)x|=(1<<i);
        }
        printf("%d\n",x);
    }
    
}
毒瘤xor
posted @ 2022-07-18 22:44  yyys  阅读(27)  评论(0编辑  收藏  举报