初中信息奥赛模拟测试

初中信息奥赛模拟测试

T1 ZEW 玩扫雷

image

\(n\)\(m\) 都是小的,枚举即可。

#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=1010;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,m,sum[N][N];
char a[N][N];
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n),read(m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i][j]=='.')
            {
                if(i-1>0&&j-1>0&&a[i-1][j-1]=='*') sum[i][j]++;
                if(i-1>0&&j>0&&a[i-1][j]=='*') sum[i][j]++;
                if(i-1>0&&j+1<=m&&a[i-1][j+1]=='*') sum[i][j]++;
                if(i>0&&j-1>0&&a[i][j-1]=='*') sum[i][j]++;
                if(i>0&&j+1<=m&&a[i][j+1]=='*') sum[i][j]++;
                if(i+1<=n&&j-1>0&&a[i+1][j-1]=='*') sum[i][j]++;
                if(i+1<=n&&j+1<=m&&a[i+1][j+1]=='*') sum[i][j]++;
                if(i+1<=n&&j>0&&a[i+1][j]=='*') sum[i][j]++;
            }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            if(a[i][j]=='.') cout<<sum[i][j];
            else cout<<a[i][j];
        cout<<endl;
    }
}
  • 注意\(a_{i,j}\)\("."\) 时,他周围可能一个地雷也没有,那么此时 \(sum_{i,j}\) 就是 \(0\)
    所以最后输出时,一定不能是
    if(sum[i][j]!=0) cout<<sum[i][j];
    
    不然所有的 \(0\) 都会输出成 \("."\),像这样:image

所以这道题爆零了 \(qwq\)

复杂度 \(O(nm)\)


T2 ZEW 的游戏

image

显然所有平行的直线中只能要一个。

那么我们可以通过 \(\dfrac{y_i-y_j}{x_i-x_j}\) 求出斜率 \(k\),所以就是求能求出的 \(k\) 的个数。

所以可以先把所有的直线的 \(k\) 都求出来,之后可以排序再去重,也可以用 \(map\)

\(map\)\(<>\) 内第一个是下标( \(key\) ),第二个是返回值( \(value\) )。

#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=1010;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,ans,tot;
double c[N*N];
struct aa
{
    int x,y;
}e[N];
map<double,bool>q;
signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n);
    for(int i=1;i<=n;i++)
        read(e[i].x),read(e[i].y);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            if(e[i].x!=e[j].x) c[++tot]=(double)(e[i].y-e[j].y)/(e[i].x-e[j].x);
            else c[++tot]=0x7f7f7f;
            if(!q[c[tot]]) ans++;
            q[c[tot]]=1; 
        }
    cout<<ans;
}

\(k\) 一定要是 \(double\),所以代码中第 \(35\) 行括号里的 \(double\) 一定要加,\(\times 1.0\) 不好使 \(qwq\)

所以也爆零了

复杂度 \(O(n^2log(n))\)


T3 ZEW 学分块

image

可以用双指针,定义 \(l,r\)

枚举第二个分割点 \(r\),由此将 \(r\) 固定住,再去搞前两个分块。

而当前两个块差值最小的时候,也就是他们两个中最大值最小的时候,那么去找到第一个左块大于右块的 \(l\) 和最后一个左块小于右块的 \(l\),选择他们之中最优的那一个,去更新 \(ans\)

通过赛时打出的暴力,我们知道每种情况的 \(ans\) 呈一个不规则的二次函数形状,所以出现第一个当前 \(ans\) 大于等于已经推出的 \(ans\) 时,就可以 \(break\) 了。

#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=5e7+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,a[N],sum[N],l,r,ans=0x7f7f7f7f;
signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n);
    for(int i=1;i<=n;i++)
        read(a[i]),
        sum[i]=sum[i-1]+a[i];
    l=1,r=2;
    while(r<n)
    {
        while(sum[l]<sum[r]-sum[l]&&l<r) l++;
        if(min({max({sum[l],sum[r]-sum[l],sum[n]-sum[r]}),max({sum[l-1],sum[r]-sum[l-1],sum[n]-sum[r]})})>=ans) break;
        ans=min({ans,max({sum[l],sum[r]-sum[l],sum[n]-sum[r]}),max({sum[l-1],sum[r]-sum[l-1],sum[n]-sum[r]})});
        r++;
    }
    cout<<ans;
}

复杂度 \(O(n)\) (光输入已经 \(O(n)\) 了)

  • 直接暴力的话超时 \(40\) 分,虽然根据他呈不规则二次函数而加了 \(n\)\(break\) 。( \(n\) 是虚数)
  • 至于数据范围中给了一个 \(1\le a_i\le 233\),所以隔壁 @\(minecraft666\) 直接暴力枚举 \(233^2\),然后 \(A\) 掉了,就很离谱
  • 当然这道题正解应是二分答案,但 \(m=3\),所以双指针也是可以的。

T4 ZEW 玩 thd

image

不难看出来是道 \(DP\)所以赛时没打出来

根据贪心思想,让塔尽可能多的打,然后人补刀,是最赚的。

由此先搞出几个变量:

对于第 \(i\) 个小兵

  • 设塔的攻击秒数为 \(t_i\),则 \(t_i\) 为满足 \(t_i\times q<h_i\) 的最大正整数值。
  • 设人的攻击秒数为 \(r_i\),则 \(r_i=\lceil\dfrac{h_i-q\times t_i}{p}\rceil\)

然后进行背包 \(DP\)

  • 人和塔的攻击是同步的,在每一秒里,塔都会进行攻击,而人可以选择攻击或不攻击。

  • 所以我们设一个 \(DP\) 常用的 \(f_{i,j}\)\(i\) 是攻打第 \(i\) 个小兵,\(j\) 是进行到第 \(j\) 秒。

  1. 如果这一个小兵不对他补刀,

    那么收到的钱就是 \(0\),则需要让塔自己负责把他打死

    此时 \(f_{i,j+t_i+1}=max(f_{i,j+t_i+1},f_{i-1,j})\)

    \(j+t_i+1\) 是第 \(i-1\) 个小兵死了之后,\(i\) 存活的秒数。

  2. 如果这个小兵我选择去补刀,

    首先需要判断能不能杀死他,即 \(j+t_i-r_i>0\)

    本来他能活到 \(j+t_i\) 秒后再死,现在人也去打他,那么他活的就更短了,所以是 \(j+t_i-r_i\)

    此时 \(f_{i,j+t_i-r_i}=max(f_{i,j+t_i-r_i},f_{i-1,j}+g_i)\),此时如果杀掉他更赚钱,就得到 \(g_i\)

最后输出 \(f_{n,j}\) 最大值即可,即在第几秒把第 \(n\) 个小兵打死。

#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=200;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
int p,q,n,h[N],g[N],sum,t[N],r[N],f[N][N*N],ans;
signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(p),read(q),read(n);
    for(int i=1;i<=n;i++)
        read(h[i]),read(g[i]),
        t[i]=(h[i]%q==0)?(h[i]/q-1):(h[i]/q),
        r[i]=ceil(1.0*(h[i]-q*t[i])/p),
        sum+=t[i]+1;
    memset(f,-0x3f,sizeof(f)),f[0][1]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=sum;j++)
        {
            f[i][j+t[i]+1]=max(f[i][j+t[i]+1],f[i-1][j]);
            if(j-(r[i]-t[i])>=0) 
                f[i][j-(r[i]-t[i])]=max(f[i][j-(r[i]-t[i])],f[i-1][j]+g[i]);
        }
    for(int i=0;i<=sum;i++) ans=max(ans,f[n][i]);
    cout<<ans;
}

而至于代码中的 \(sum\) 就是总共可以用的时间,而这个 \(sum+=t[i]+1\) 如何理解,虽然在这一秒小兵死定了,但和 \(PvZ\) 一样,他死了也要在那儿站一秒,消耗一秒时间去打他,他才死透了。

这个 \(n\) 这么小就不要去考虑复杂度了嘛


论爆零

\(T1,T2\) 全都爆零了,导致这次模拟赛只有 \(70\) 分。考完情绪异常崩溃,恰好赶上放假,直接提前离校了。

而爆零的原因也是那么抽象,只是几个字母的区别就从 \(100\) 直接变成 \(0\),当时非常抱怨这场比赛为什么值提供一个样例,但凡和 \(\text{CSP}\) 一样再给个大样例也不会这样的啊。

而恰恰就是这样的,对于自己平时计算就没怎么算对过,和这次爆零也就差不多。

平时做题一直是很快,不管 \(\text{OI}\) 还是数学作业,但是很少全对,因为计算算不对。\(T1,T2\) 一共用了 \(10\) 分钟,\(T1\) 还没断网就交了,然而是爆零的。

也是心态的问题,太紧张了,把这次考试看的太重要了。

主要还是惧怕中考,看样子还是要中考了,之前再怎么也没用了。

一直以为我们这组是没有理由要别人的,作为一个新生,又有谁能半年就拿 \(\text{CSP}\) 一等,然而不认真的性格导致我大概率与直升无缘了。

所以再怎么想也是没用的了,只能当做一次教训了,以后,认真点,别再爆零了,文化课也是,不要因为低错一扣十几分了。

体育也是改革了,中考还是有希望的……

所以继续加油吧,我和 \(\text{OI}\) 的故事不会到此就结束的。

原来 咽回去的泪

才能 淹没了脆弱

你 发誓更勇敢 一生与梦相拥

还 想要继续吗

要 逆风不退啊

让痛苦住进 你眼眸

别怕 未来的模样

辜负 曾经的凝望

有 多少理想 就有 多少次传唱

那 沿途的风浪

也 不过就这样

这一路光景 有你在身旁

要炙热啊 要不忘啊 要勇往啊

起风了 呼唤了 你还在等什么

挥手了 再见了 别不舍回头啊

向心中的梦啊 去追吧

要能留下伤痕才算活过

愿你记得 愿你哭过 不惧脆弱

posted @ 2024-01-14 21:15  卡布叻_周深  阅读(55)  评论(6编辑  收藏  举报