六一欢乐赛 考试总结

时间:2018年5月31日 8:00~11:00

题目:5

难度:普及+/提高-

T1、T2略

T3:

题目:

此时,Conan 却在一旁玩着 2048。

这是一个 4*4 的矩阵,初始全为 0。每次一个没有数字的格子中会随机出现一个 2 或 4,每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,相同数字的方块在靠拢、相撞时会相加。

Conan 想看看今天自己运气咋样,于是就闭着眼睛,在屏幕上随便滑来滑去。所以这个模拟的任务就交给你了。过了一会,他然后睁开眼睛,如果游戏没有结束(滑动后如果没有空格子,则游戏结束),请输出矩阵(格式参见样例),否则输出“Game over!”(不包含引号)。

1.游戏结束的含义:如果当前位置的数并没有消掉,此位置又新出现数了,游戏结束2.向右滑,比如8 2 2 2会消成8 2 4 向左滑,会消成8 4 2 2 2 2 2向右滑,会是4 4,而不是8

N <=1000。

分析:

题意:满了,还有操作,就game over

以2048为背景的模拟题。需要考虑的情况不多,但是还是漏掉了情况。每次移动,先判断game over 。消除数字的时候,最好先让数字到位,再消除。再合并。

想要一边归位一边消除的时候,容易造成:4 4 2 2 (右滑)-> 4 4 0 4 -> 4 0 0 8 -> 0 0 4 8。因为每一位归位后判断是否消除都是和上个归位的比较是否相同判断。

但是实际上,4 4 2 2 -> 0 0 8 4 因为每个数只能在一轮发生一次合并。

可以改进的是,判断在这一个位置上是否已经发生“碰撞”,没发生才行。

如果先到位再判断的话: 4 4 2 2 -> 4 4 2 2  ( 归位 )-> 4 4 0 4 (j=4&&j=3) -> 4 4 0 4 (j=3&&j=2) -> 0 8 0 4 (j=2&&j=1) -> 0 0 8 4 (归位) 就不会有问题了。

总结:还是有一些方面没有想到位,确实没有什么好方法。只能是根据题目内容信息,仔细把握,抓住细节。数据可以构造地活一点。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1000+10;
int n,siz;
int mp[6][6];
int a[6][6];
bool vis[6];//注意这个vis
bool flag=true;
struct node{
    int x,y,v;
    char f;
}q[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d%d %c",&q[i].x,&q[i].y,&q[i].v,&q[i].f);
    for(int i=1;i<=n;i++)
    {
        siz++;
        if(siz>16) {
            flag=false;break;
        }//warning
        mp[q[i].x][q[i].y]=q[i].v;
        memset(a,0,sizeof a);
        if(q[i].f==‘U‘){
            for(int j=1;j<=4;j++)
            {
             memset(vis,0,sizeof vis);
             int k=1;
             for(int i=1;i<=4;i++)
             {
                 if(mp[i][j])
                {
                    a[k][j]=mp[i][j];
                     if(k>1&&a[k-1][j]==a[k][j]&&!vis[k-1]) a[k-1][j]=2*a[k-1][j],vis[k-1]=1,a[k][j]=0,k--;
                     k++;
                 }
             }
            }
        }
        else if(q[i].f==‘D‘){
            for(int j=1;j<=4;j++)
            {
                memset(vis,0,sizeof vis);
             int k=4;
             for(int i=4;i>=1;i--)
             {
                 if(mp[i][j])
                {
                    a[k][j]=mp[i][j];
                     if(k<4&&a[k+1][j]==a[k][j]&&!vis[k+1]) a[k+1][j]=2*a[k+1][j],vis[k+1]=1,a[k][j]=0,k++;
                     k--;
                 }
             }
            }
        }
        else if(q[i].f==‘L‘){
            for(int i=1;i<=4;i++)
            {
                memset(vis,0,sizeof vis);
             int k=1;
             for(int j=1;j<=4;j++)
             {
                 if(mp[i][j])
                {
                    a[i][k]=mp[i][j];
                     if(k>1&&a[i][k-1]==a[i][k]&&!vis[k-1]) a[i][k-1]=2*a[i][k-1],vis[k-1]=1,a[i][k]=0,k--;
                     k++;
                 }
             }
            }
        }
        else{
            for(int i=1;i<=4;i++)
            {
                memset(vis,0,sizeof vis);
             int k=4;
             for(int j=4;j>=1;j--)
             {
                 if(mp[i][j])
                {
                    a[i][k]=mp[i][j];
                     if(k<4&&a[i][k+1]==a[i][k]&&!vis[k+1]) a[i][k+1]=2*a[i][k+1],vis[k+1]=1,a[i][k]=0,k++;
                     k--;
                 }
             }
            }
        }
        memcpy(mp,a,sizeof a);
        siz=0;
        for(int i=1;i<=4;i++)
         for(int j=1;j<=4;j++)
          if(mp[i][j]) siz++;
    }
    if(!flag){
        printf("Game over!");
    }
    else{
        for(int i=1;i<=4;i++)
        {
         for(int j=1;j<=4;j++)
          {
           if(j!=4) printf("%d ",mp[i][j]);
           else printf("%d",mp[i][j]);
          }
         puts("");
        }
    }
    return 0;
}

T4:

题目:

豪华礼包:一个 U 盘、一个鼠标和一个机械键盘。

幸运礼包:一个 U 盘、两个鼠标。

普通礼包:两个 U 盘、一个鼠标。

卖店内准备了 a 个 U 盘、b 个鼠标和 c 个机械键盘。为了给顾客带来足够多的惊喜,店长希望相邻两位领礼包的顾客拿到的礼包类型都是不同的。店长想知道这些奖品最多可以发出多少份礼包。

组数T <=100000 ,a,b, c <=1,000,000 。

分析:

看似背包实际范围太大。看数据范围,可以做到 tlogmx

O1 公式不好想。就加一个log , 最直观的想法应该是二分(虽然没有想到)。单调性是显然的。

那么对于每一个二分的答案,我们必须O1判断。首先,发现所有的礼包可以划分成:(a+b)+c,(a+b)+a,(a+b)+b。必有a+b

先判断a+b够不够用。减去这次判断用掉的a+b之后,看剩下的a/b/c能否凑够mid份。剩下的:la=mid-a,lb=mid-b,lc=c;

la+lb+lc<mid 可以返回false

但是有个条件是:相邻两个不一样,也就是说,用掉的任意两个物品的总数和,+1后不能小于剩下一个物品的用掉的数量。

但是,la,lb,lc可能剩的多,怎样判断用掉的有多少呢?

a/b/c最多都再用掉ceil(mid/2)份。所以,每一个和mid-a取min

这样,保证当a、b、c足够的时候,mid是一个合法解。

总结:

二分实在没想到。可以反省的是,开始可以多想几个思路。没有思路的时候,应该根据数据范围先想出来时间复杂度。再套算法。

也许这样,二分就能想到了。

二分其实简化在于:在符合单调性的情况下,用一个log的代价,知道了答案,从而逆向考虑这个答案是否合法。把正向的找答案,变成了逆向的把所有的答案都试一遍。和选择题带入选项的思路类似。

#include<bits/stdc++.h>
using namespace std;
int t,a,b,c;
bool er(int x)
{
    if(a<x||b<x) return false;
    int la=min((int)ceil(1.0*x/2),a-x),lb=min((int)ceil(1.0*x/2),b-x),lc=min(c,(int)ceil(1.0*x/2));
    if(la+lb+lc<x) return false;
    if(la+lb+1<lc||lb+lc+1<la||lc+la+1<lb) return false;
    return true;
}
int ans,l,r;
int main()
{
    cin>>t;
    while(t--){
        scanf("%d%d%d",&a,&b,&c);
        r=a+b+c;
        l=0;
        ans=0;
        while(l<=r){
            int mid=(l+r)/2;
            if(er(mid)) ans=max(ans,mid),l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

T5:

题目:http://192.168.14.142/problem/2333

大家都说要劳逸结合,Ayumi, Mitsuhiko, Genta 画完方格就出去运动啦!

他们来到了一片空地,画了 N 个连续的方格,每个方格上随机填上了一个数字,大家从第一个格子开始,每次可以向后跳不超过当前格子上的数的步数,大家开始就此比赛,看谁跳到最后一个格子的步数最少。

输出一行,表示跳的最少步数。N <= 10^7,a[i]<=5000

分析:

看复杂度。也许O(n), 好像加一个log也没事。大概确定了循环顺序,估计要O(n)扫一遍。

我们设f[i]表示,到i这个点最小步数。n^2显然可以。

如果我要log 转移呢?

仍然逆向考虑,类似于最长上升子序列,我们考虑,

假如已经知道了,p[j] : 走j步最多到哪个位置,那么,假如循环到i这个位置,(之前能到达i位置的点都已经处理过了,更新了p)我们只要找到能到达i的最少j

发现,这个p[j]一定是单调的。又是二分!!

具体实现方法:

1.初始f[1]=0,p[1]=1+a[1];其他p先设为0x3f3f3f(虽然不合定义,但是方便lower_bound)

2.然后,循环从i=2开始循环,先通过lower_bound 找到第一个>=i的p[j]的j,这就是f[i]的值。因为i之前的点已经尝试更新过p了,那么,现在的p数组,就是目前,每个步数所能到达的最远地方。

3.将p[f[i]+1]=max(p[f[i]+1],i+a[i]) 表示,如果用最小步数先到达i,再多走一步,最多到达a[i]+i,是否可以更新p

因为之前设了p=0x3f3f3f3f,所以特判一下就好。

4.输出f[n]

总结:

不好想。只能说,还是先看数据范围,想复杂度,然后就神仙一般想到了“i步最远能到哪里”。可以说是对平时算法的转化应用吧。

记得单调栈求最长上升子序列的时候,我们也是用了一个数组g,g[i]表示,长度为i的最长上升子序列最后一个数最小是多少。有一点贪心的意思。

平时对于每个题目,每个算法一定要仔细琢磨。尝试串联起来。

#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
const int inf=0x3f3f3f3f;
int n,x;
int p[N],f[N];
int a[N];
inline int gen(int x){
    return (1LL*x*(x^(x>>1))+(x-3)*(x|(x<<1)))%min(n,5000)+1;
}
int main(){
    scanf("%d%d",&n,&x);
    for(int i=1;i<=n;++i)
        x=gen(x),a[i]=x;
    memset(p,inf,sizeof p);
    p[1]=1+a[1];
    for(int i=2;i<=n;i++)
    {
        f[i]=lower_bound(p+1,p+n+1,i)-p;
        if(p[f[i]+1]!=inf) p[f[i]+1]=max(p[f[i]+1],i+a[i]);
        else p[f[i]+1]=i+a[i];
    }
    printf("%d",f[n]);return 0;
}

原文地址:https://www.cnblogs.com/Miracevin/p/9117926.html

时间: 2024-11-02 17:09:34

六一欢乐赛 考试总结的相关文章

Comet OJ - 2019 六一欢乐赛

传送门 #A: 思路:等差数列求和,看成俩次1+2+…+ n,多加的n减去,所以 ans = n*(n+1) - n. AC代码: 1 #include<iostream> 2 #include<algorithm> 3 #include<string> 4 using namespace std; 5 int main() 6 { 7 int n; 8 while(cin >> n) 9 { 10 cout << n*(n+1) - n <

欢乐赛解题报告

~~一场不欢乐的欢乐赛 时间分配::T1做的时候还可以,大约三十分钟写了个深搜(鬼知道我为啥不用广搜,大概是因为快半个月没写了)写完后去研究第二题,刚开始以为是贪心,很快写了出来,但是自己推了一会举出了反例.于是自己想了很多方法,但是都是基于贪心,写了一个多小时,写炸了,没办法又改成了贪心.第三题并不会,然后搜索大法过了一个点,(输出-1也是一个点) 整体感觉::还是太弱,T1是会的,但是还是没做对,大概是独立做题少的缘故吧,平常做题都没有思考太多时间.T2贪心T3暴力,貌似自己啥都不会.到现在

综合-某假期欢乐赛 (Apri, 2018)

假期欢乐赛,确实挺轻松的,被逼迫写了题解. A.推数按列观察,有的列有多个格子,看起来好复杂啊,先放一放.按行观察,黑色格子在 i 行 j 列:当 i 是奇数,对应数字第 i 位是 j-1当 i 是偶数,对应数字第 i 位是 9-j B.体重某位同学不是中间体重的充要条件是,比他重的人数 >= mid 或 比他轻的人数 >= midx 比 y 重,则 x, y 有单向连通关系,G[x][y] = true .比 y 重的人数就是满足 G[i][y] = true 的 i 的数量.比 y 轻的人

NOIP模拟 (8-2情人节欢乐赛) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目来自hzwer的模拟题 果实计数 (count.pas/.c/.cpp) 时间限制:1s,空间限制32MB 题目描述: 淘淘家有棵奇怪的苹果树,这棵树共有n+1层,标号为0~n.这棵树第0层只有一个节点,为根节点.已知这棵树为b叉树,且保证是一颗满b叉树. 现在,该树第n层的每个节点上都结出了一个苹果,淘淘想知道共结了多少苹果.由于数量可能很大,答案要求输出mod k后的结果. 输入描述: 给出第1层的节点数b和层数n和k

2017端午欢乐赛——Day1T3(树的直径+并查集)

//前些天的和jdfz的神犇们联考的模拟赛.那天上午大概是没睡醒吧,考场上忘了写输出-1的情况,白丢了25分真是**. 题目描述     小C所在的城市有 n 个供电站,m条电线.相连的供电站会形成一个供电群,那么为了节省材料,供电群是一棵树的形式,也即城市是一个森林的形式(树:V个点,V-1条边的无向连通图,森林:若干棵树).每个供电群中不需要所有供电站都工作,最少只需要一个工作,其余的就都会通过电线收到电,从而完成自己的供电任务.当然,这样会产生延迟.定义两个供电站的延迟为它们之间的电线数量

【学长虐学弟欢乐赛系列】Round4

第四套题 出题人:Bakser 神犇地址:Bakser学长的blog 试题下载地址 数据下载地址 T1 LICS 有一天 Bakser 在做数学题,对于一道题他验算了 n 遍,得到了 n 个结果.无聊的他将这 n 个结果排成了一个圆环,无聊的他又想求这 个环的最长上升子序列. 请你回答这个无聊的问题. 所谓环的上升子序列,指的是从某个位置开始, 按照顺时针顺序 读这个环,得到一个线性序列,它的上升子序列也是这个环的上升子 序列.最长上升子序列是它们中最长的一个. 输入格式: 第一行一个数 n,表

暑假爆零欢乐赛SRM08题解

这真的是披着CF外衣的OI赛制?我怎么觉得这是披着部分分外衣的CF?果然每逢cf赛制必掉rating,还是得%%%cyc橙名爷++rp.. A题就是找一找序列里有没有两个连在一起的0或1,并且不能向两端延伸(比如--1001--或110--或者--100),找到了之后就可以把整个序列分成这两个数左边,这两个数和他的右边三部分,然后这两个0或1每个都能与左边右边串在一起构成两个相同的子序列,并且这个子序列在原序列中只会出现这两次,满足题目条件.如果没找到,再看看原来的序列里是不是只有两个0或1,那

【20170311】白色情人节欢乐赛

我出的水题,因为是白色情人节前一天,所以题目背景.. 试题:/s/1pKKkCeJ         vyh3 (百度云) T1:chocolate 题意:给你n个妹子,每个妹子对你的初始好感度为c0[i],你不给她x块巧克力她的好感度会下降c1点,你给她y块巧克力她的好感度会上升c2点,现在问你,你有m块巧克力的情况下你可以获得的好感度之和最大是多少. 解题思路:显然是个01背包类型的DP.时间效率O(nm). 标程: #include<cstdio> #include<iostream

CH Round #56 - 国庆节欢乐赛解题报告

最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树,其中一些树上结有能够产生能量的魔力水果.已知每个水果的位置(Xi,Yi)以及它能提供的能量Ci.然而,魔幻森林在某些时候会发生变化:(1) 有两行树交换了位置.(2) 有两列树交换了位置.当然,树上结有的水果也跟随着树一起移动.不过,只有当两行(列)包含的魔力水果数都大于0,或者两行(列)都没有魔