20150714学校测试

  • 前言

    4个题里有两个SCOI的题,还都是2005年的。

  • 题目
  • 一、整数的表示

    任何一个正整数都可以用2的幂次方表示.例如:137=27+23+20同时约定次方用括号来表示,即ab可表示为a(b)由此可知,137可表示为:2(7)+2(3)+2(0)进一步:7=22+2+20(21用2表示)3=2+20所以最后137可表示为:2(2(2)+2+2(0))+2(2+2(0))+2(0)又如:1315=210+28+25+2+1所以1315最后可表示为:2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

    输入文件:

    只有一行,就是正整数n(n≤20000)

    输出文件:

    只有一行,就是符合约定的n的0,2表示(在表示中不能有空格)

  • 二、排数问题:

    设有n个正整数,将他们连接成一排,组成一个最大的多位整数。例如:n=3时,3个整数13,312,343,连成的最大整数为:34331213。又如:n=4时,4个整数7,13,4,246连接成的最大整数为7424613。

    输入文件:

    输入文件有N+1行,第一行为整数N(≤100)。此后N行,每行输入一个正整数(≤32767)。

    输出文件:

    输出:连接成的多位数。

    输入输出举例:

    输入arra.in:

    3

    13

    312

    343

    输出arra.out:

    34331213

  • 三、 骑士精神(Knight)

    在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。

    给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:

    为了体现出骑士精神,他们必须以最少的步数完成任务。

    输入文件:

    第一行有一个正整数T(T≤10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

    输出文件:

    对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

    输入输出举例:

    Knight.in:

    2

    10110

    01*11

    10111

    01001

    00000

    01011

    110*1

    01110

    01010

    00100

    Knight.out

    7

    -1

  • 四 扫雷 (Mine)

    相信大家都玩过扫雷的游戏。那是在一个n*m的矩阵里面有一些雷,要你根据一些信息找出雷来。万圣节到了,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字表示和它8连通的格子里面雷的数目。现在棋盘是n×2的,第一列里面某些格子是雷,而第二列没有雷,如下图:

    由于第一列的雷可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息确定第一列雷有多少种摆放方案。

    输入文件:

    第一行为N,第二行有N个数,依次为第二列的格子中的数。(1≤N≤10000)

    输出文件:

    一个数,即第一列中雷的摆放方案数。

    输入输出举例:

    Mine.in:

    2

    1

    Mine.out

    2

  • 题解和代码
  • 第一题一看就是递归处理。递归中最重要的一环就是设计好边界,本题中当n=20时输出2(0),n=21时输出2,其余的就可以把n进行二进制分解再分别递归了。要注意何时输出+号。这里可以用树状数组里的低位技术(lowbit)在O(log2n)的时间里完成分解。实测Codevs中(n约为1012)用了7ms,很快。分解过后的数可以储存在一个栈里,把栈弹到只剩一个元素时就不要输出+号了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
void init()
{
    freopen("number.in", "r", stdin);
    freopen("number.out", "w", stdout);
    scanf("%d", &n);
}
inline int f(int x)
//求指数
{
    int y = -1;
    while(x != 0)
    {
        ++y;
        x >>= 1;
    }
    return y;
}
void work(int k)
{
    int *s = new int[20], top = 0;
    memset(s, 0, sizeof(s));
    while(k != 0)
    {
        s[++top] = (k & (-k));
        k -= (k & (-k));
    }
    for(int i = top; i > 1; --i)
    {
        if(f(s[i]) == 0)
        {
            printf("2(0)");
        }
        else
        {
            if(f(s[i]) == 1)
            {
                printf("2");
            }
            else
            {
                printf("2(");
                work(f(s[i]));
                printf(")");
            }
        }
        printf("+");
    }
    if(f(s[1]) == 0)
    {
        printf("2(0)");
    }
    else
    {
        if(f(s[1]) == 1)
        {
            printf("2");
        }
        else
        {
            printf("2(");
            work(f(s[1]));
            printf(")");
        }
    }
    delete [] s;
    s = NULL;
}
int main()
{
    init();
    work(n);
    return 0;
}
  • 第二题是很明显的贪心,但有两种常见的错误思路:

    把大的数放前面。反例,9、13,913>139;

    把短的数放前面。反例,12、123,12312>12123。我一开始就犯了这个错误。

    其实只要把a和b这两个数按ab和ba的方式分别放一下,然后若ab>ba,则把a放b前面(可以不相邻);若ab<ba,则把b放a前面(也可以不相邻)。这样自定义一个比较函数,快排即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct number
{
    int num;
    bool operator < (number b) const
    {
        int *p = new int[20], *bp = new int[20];
        int top = 0, btop = 0, k = num;
        bool flag = false;
        memset(p, 0, sizeof(p));
        memset(bp, 0, sizeof(bp));
        //分解每一位
        while(k > 0)
        {
            p[++top] = k % 10;
            k /= 10;
        }
        while(b.num > 0)
        {
            bp[++btop] = b.num % 10;
            b.num /= 10;
        }
        long long s = 0, bs = 0;
        //按ab方式连
        for(int i = top; i > 0; --i)
        {
            s = s * 10 + p[i];
        }
        for(int i = btop; i > 0; --i)
        {
            s = s * 10 + bp[i];
        }
        //按ba方式连
        for(int i = btop; i > 0; --i)
        {
            bs = bs * 10 + bp[i];
        }
        for(int i = top; i > 0; --i)
        {
            bs = bs * 10 + p[i];
        }
        if(s > bs)
        {
            flag = true;
        }
        delete [] p;
        delete [] bp;
        p = bp = NULL;
        return flag;
    }
};
number a[105];
int n;
int main()
{
    freopen("arra.in", "r", stdin);
    freopen("arra.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i].num);
    }
    sort(a + 1, a + n + 1);
    for(int i = 1; i <= n; ++i)
    {
        printf("%d", a[i].num);
    }
    return 0;
}
  • 第三题一眼看上去是一道广搜,但算了一下状态数,发现最多到了815……于是乎想到启发式迭代加深,即IDA*。然而不幸的是没*对,全都-1。后来把启发函数改成了普通的可行性剪枝(就是代码中残留的h函数,它的返回值一开始是int的),然后竟然没T。IDA*->dfsID……实际上这个可行性剪枝是非常强的,当前步数加上和标准答案不一致的位置数如果超过了总步数,那么一定无解。因为走一步顶多把一个骑士的位置修正到正确位置。
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int t[5][5] = {{1, 1, 1, 1, 1}, {0, 1, 1, 1, 1}, {0, 0, -1, 1, 1}, {0, 0, 0, 0, 1}, {0, 0, 0, 0, 0}};
const int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}, dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int T, K, sx, sy;
int s[5][5];
bool flag;
void init()
{
    char a[10];
    flag = false;
    for(int i = 0; i < 5; ++i)
    {
        scanf("%s", &a);
        for(int j = 0; j < 5; ++j)
        {
            if(a[j] == ‘*‘)
            {
                sx = i; sy = j;
                s[i][j] = -1;
            }
            else
            {
                s[i][j] = a[j] - ‘0‘;
            }
        }
    }
}
bool check(int p[5][5])
{
    for(int i = 0; i < 5; ++i)
    {
        for(int j = 0; j < 5; ++j)
        {
            if(p[i][j] != t[i][j])
            {
                return false;
            }
        }
    }
    return true;
}
bool h(int now[5][5], int nowk)
{
    int k = 0;
    for(int i = 0; i < 5; ++i)
    {
        for(int j = 0; j < 5; ++j)
        {
            if(now[i][j] != t[i][j])
            {
                ++k;
                if(k + nowk > K)
                {
                    return false;
                }
            }
        }
    }
    return true;
}
void dfs(int now[5][5], int x, int y, int nowk)
{
    if(nowk == K)
    {
        if(check(now))
        {
            flag = true;
            return;
        }
    }
    if(flag)
    {
        return;
    }
    for(int i = 0; i < 8; ++i)
    {
        int nx = x + dx[i], ny = y + dy[i];
        if(nx >= 0 && ny >= 0 && nx < 5 && ny < 5)
        {
            swap(now[x][y], now[nx][ny]);
            if(h(now, nowk))
            {
                dfs(now, nx, ny, nowk + 1);
            }
            swap(now[x][y], now[nx][ny]);
        }
    }
}
int main()
{
    freopen("knight.in", "r", stdin);
    freopen("knight.out", "w", stdout);
    scanf("%d", &T);
    while(T--)
    {
        init();
        for(K = 1; K <= 15; ++K)
        {
            dfs(s, sx, sy, 0);
            if(flag)
            {
                printf("%d\n", K);
                break;
            }
        }
        if(!flag)
        {
            puts("-1");
        }
    }
    return 0;
}
  • 第四题看上去像dp计数问题,但一开始并没有想到如何转移,于是先写了个暴搜。起初为了便于以后对拍大数据,就先预处理了一下必须放雷的格子,然后加了若干可行性剪枝。后来跑大数据时发现挺快的,于是交到了bzoj上,结果20msAC。省选题啊!暴搜都能过?
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, a[10005], ans, c[10005];
bool f[10005];
void init()
{
    freopen("mine.in", "r", stdin);
    freopen("mine.out", "w", stdout);
    memset(f, 0, sizeof(f));
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
        if(a[i] == 3)
        {
            f[i - 1] = f[i] = f[i + 1] = true;
        }
    }
    if(a[1] == 2)
    {
        f[1] = f[2] = true;
    }
    if(a[n] == 2)
    {
        f[n] = f[n - 1] = true;
    }
}
void dfs(int k, bool b)
//该放第k个位置,同时第k-1个位置有没有放雷
{
    if(k > n && c[k - 1] == a[k - 1])
    {
        ++ans;
        return;
    }
    if(c[k - 2] != a[k - 2])
    {
        return;
    }
    if(c[k - 1] < a[k - 1] && c[k] < a[k])
    {
        ++c[k - 1]; ++c[k]; ++c[k + 1];
        dfs(k + 1, true);
        --c[k - 1]; --c[k]; --c[k + 1];
    }
    if((!f[k]) && (c[k - 1] == a[k - 1]))
    {
        dfs(k + 1, false);
    }
}
void work()
{
    ++c[1]; ++c[2];
    dfs(2, true);
    --c[1]; --c[2];
    dfs(2, false);
    printf("%d\n", ans);
}
int main()
{
    init();
    work();
    return 0;
}

后来想到了一种方式:f[i][0||1][0||1][0||1]第一维表示递推到第i个位置,第二维表示i-1位置的放雷情况,第三维是i位置放雷情况,第四维i+1位置。这样方程就非常清晰(详见代码)。

只是要注意1的左边和n的右边是不能放雷的。

#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[10005], f[10005][2][2][2];
void init()
{
    freopen("mine.in", "r", stdin);
    freopen("mine.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
    }
    switch(a[1])
    {
        case 0: f[1][0][0][0] = 1;
                break;
        case 1: f[1][0][1][0] = f[1][0][0][1] = 1;
                break;
        case 2: f[1][0][1][1] = 1;
                break;
        default:break;
    }
}
void work()
{
    for(int i = 2; i < n; ++i)
    {
        switch(a[i])
        {
            case 0: f[i][0][0][0] = f[i - 1][1][0][0] + f[i - 1][0][0][0];
                    break;
            case 1: f[i][1][0][0] = f[i - 1][1][1][0] + f[i - 1][0][1][0];
                    f[i][0][1][0] = f[i - 1][1][0][1] + f[i - 1][0][0][1];
                    f[i][0][0][1] = f[i - 1][1][0][0] + f[i - 1][0][0][0];
                    break;
            case 2: f[i][1][1][0] = f[i - 1][1][1][1] + f[i - 1][0][1][1];
                    f[i][1][0][1] = f[i - 1][1][1][0] + f[i - 1][0][1][0];
                    f[i][0][1][1] = f[i - 1][1][0][1] + f[i - 1][0][0][1];
                    break;
            case 3: f[i][1][1][1] = f[i - 1][1][1][1] + f[i - 1][0][1][1];
                    break;
            default:break;
        }
    }
    int ans = 0;
    switch(a[n])
    {
        case 0: ans += f[n - 1][1][0][0] + f[n - 1][0][0][0];
                break;
        case 1: ans += f[n - 1][1][1][0] + f[n - 1][0][1][0];
                ans += f[n - 1][1][0][1] + f[n - 1][0][0][1];
                break;
        case 2: ans += f[n - 1][1][1][1] + f[n - 1][0][1][1];
                break;
        default:break;
    }
    printf("%d\n", ans);
}
int main()
{
    init();
    work();
    return 0;
}
  • 总结

    暂时没思路时要毫不犹豫地先把搜索写上,再没思路就专心优化自己的搜索。遇到简单的题目更要细心,一旦想错了就什么都完了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 17:26:03

20150714学校测试的相关文章

20150714学校測试

前言 4个题里有两个SCOI的题.还都是2005年的. 题目 一.整数的表示 不论什么一个正整数都能够用2的幂次方表示.比如:137=27+23+20同一时候约定次方用括号来表示,即ab可表示为a(b)由此可知,137可表示为:2(7)+2(3)+2(0)进一步:7=22+2+20(21用2表示)3=2+20所以最后137可表示为:2(2(2)+2+2(0))+2(2+2(0))+2(0)又如:1315=210+28+25+2+1所以1315最后可表示为:2(2(2+2(0))+2)+2(2(2

学校测试-2015-2-27

题目一 描述 关键子工程(project.c/cpp/pas) 在大型工程的施工前,我们把整个工程划分为若干个子工程,并把这些子工程编号为1.2.--.N:这样划分之后,子工程之间就会有一些依赖关系,即一些子工程必须在某些子工程完成之后才能施工.由于子工程之间有相互依赖关系,因此有两个任务需要我们去完成:首先,我们需要计算整个工程最少的完成时间:同时,由于一些不可预测的客观因素会使某些子工程延期,因此我们必须知道哪些子工程的延期会影响整个工程的延期,我们把有这种特征的子工程称为关键子工程,因此第

学校测试-2015-03-01

记得以前做N皇后问题见到过二进制+位运算优化的方法, 今天的搜索题第三题和第四题都可以用到二进制和位运算. 就只做了这两个题目. 题目三 描述 传递游戏(pass) Description n个人在做传递物品的游戏,编号为1-n. 游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位:下一个人可以传递给未接过物品的任意一人. 即物品只能经过同一个人一次,而且每次传递过程都有一个代价:不同的人传给不同的人的代价值之间没有联系: 求当物品经过所有n个人后,整个过程的最小

国外的一些测试技术网站

http://bdonline.sqe.com/ 一个关于网站测试方面的网页,对这方面感兴趣的人可以参考 http://citeseer.nj.nec.com/ 一个丰富的电子书库,内容很多,而且提供著作的相关文档参考和下载,是作者非常推荐的一个资料参考网站 http://groups.yahoo.com/group/LoadRunner 性能测试工具LoadRunner的一个论坛 http://groups.yahoo.com/grorp/testing-paperannou-nce/mess

《精通移动app测试实战:技术、工具和案例》新书上市

本书是测试专家.性能测试专家.专业畅销书作者--于涌,多年实战经验的总结,涵盖主流的测试工具,包括众多的测试实例,涵盖单元测试.功能测试.性能测试.UI测试.手游测试.自动化测试.测试用例管理.持续集成等移动测试中用到的所有实战技术,是一本贴近实战的移动端测试参考大全.本书主要内容如下. 书中讲解了单元测试,介绍了JUnit框架.单元测试实施.创建基于Android的测试项目和应用JUnit对Android项目进行单元测试:讲解了Android 提供的一个通用的调试工具ADB,借助这个工具,可以

正则表达式 和 junit测试

需要知道一些常规的正则表达式语句,然后可以仿照规则写出一下正则表达式语句.然后是关于junit测试. 知道了一个之前看过的文档,然后有功夫就看一下那个文档就可以,或者后面找时间搜索一下. 正则表达式是一个字符串: 由^开头 由$结尾. []表示可取值的范围. \\d表示数字. 下面两个表达式等效: ^[0-9]*$ ^\\d*$ 都表示若干数字 下面这个表示多个汉字: ^[\u4e00-\u9fa5]{0,}$ 汉字的码集是从\u4e00-\u9fa5 所有英文字母和数字: ^[a-zA-Z0-

看看你的苹果手机真的是5s,4s?看完之后测试下自己手机,让自己用得明明白白!

此文章不接触技术含量属于给普通,手机使用者判定!技术高手绕道. 看见标题了吧!现在我就来给你们鉴定下,各位朋友手上面拿着的手机是不是真的是不是5s.或者4s    都能同理可得. 今天无聊听见了,朋友说现在的苹果一千五就能拿到5s  .当时我一听见这个消息真是个迫不及待的想要见识一下这让人梦寐以求的装B神器居然如此廉价.这世界肿么了 稍微感叹一下回归正题! 首先各位朋友先是已经买了的或者还没买的打算买的,接下来注意了! 各位朋友你们到店铺里面首先是打算几s是吧?然后是什么颜色对吧?或者是选多少G

菜鸟的初行动——学校某代码评测服务器攻略战

0x00 情况简介 身为安全菜鸟爱好者的笔者本学期有一门课程, 需要提交代码到服务器上然后评测. 可恶的是服务器只会告诉你结果正误,而不返回程序的输出,因此无法通过打印输入的方法来获得测试点信息. 看着绞尽脑汁考虑了无数情况也依旧没过的几个测试点, 我不禁动了拿到测试点的想法,于是…… 0x01 屏幕 现在的难题在于获得程序的输出结果. 在动手之前,先尽可能的收集服务器的信息, 这一步也有一些收获. 2.1 收集信息 通过上交了一个包含了math库的c文件,拿到了服务器的反馈——编译错误, 而且

国外的一些测试技术网站(转载)

国外的一些测试技术网站 http://bdonline.sqe.com/ 一个关于网站测试方面的网页,对这方面感兴趣的人可以参考 http://citeseer.nj.nec.com/ 一个丰富的电子书库,内容很多,而且提供著作的相关文档参考和下载,是作者非常推荐的一个资料参考网站 http://groups.yahoo.com/group/LoadRunner 性能测试工具LoadRunner的一个论坛 http://groups.yahoo.com/grorp/testing-paperan