基础算法测试test20170326

前言

这次考试竟然出现了许多玄学的情况,请听我下面一一列举…

注:这次考试所有的题目均为 时限:1S 空限:256M

1、无聊的军官(officer.pas/c/cpp)

【问题描述】
每个学年的开始,高一新生们都要进行传统的军训。今年有一个军训教官十分奇怪,他为了测试学员们的反应能力,每次吹哨后学员们都会变换位置。每次左数第i位学员都会站到第ai个位置,经过若干次之后,队伍又会回到原来的样子。
你的任务是计算n个人的队伍至少经过多少次之后,队伍恢复到原来样子。
输入officer.in
输入文件的第一行包含一个整数N( 0N10000 ),表示队伍的人数。
接下来N行,每行一个正整数ai表示左起第i个人接下来出现在左起第ai个位置上。
输出officer.out
仅包括一行,一个正整数M,表示军官最少的吹哨次数。
【样例输入】
5
2
3
4
5
1
【样例输出】
5
【数据规模】
对于30%的数据,有 N100
对于100%的数据,有 N10000 ;
对于全部数据,答案在均在64位整数范围之内。

【题解】

这个题目记得好像是一道NOI导刊上的题目(也有可能记错了),总之这道题并不是很难,考场上大家的实际得分也证明如此。

其实这道题目,只需要根据i和ai的关系建立图形,求出整个图中所有的环的长度,再最后求最小公倍数即可。

【代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;
const int size = 10000+10;
bool b[size];
LL n,ans,Gcd,step,next[size];

LL gcd(LL a,LL b);
void dfs(LL x);

int main() {
    freopen("officer.in","r",stdin);
    freopen("officer.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    scanf("%lld",&next[i]);
    for(int i=1;i<=n;i++)
    if(!b[i]) {
        step=0; dfs(i);
        if(ans==0) ans = step;
        else {
        Gcd = gcd(ans,step);
        ans=(ans*step)/Gcd;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

inline LL gcd(LL a,LL b) {
 return b==0 ? a : gcd(b,a%b);
}

inline void dfs(LL x) {
 b[x] = true;
 step++;
 if(b[next[x]]) return;
 else dfs(next[x]);
}

2、拯救save.pas/c/cpp

【问题描述】

正义之士被恶魔抓了,被关在小黑屋里,无法继续他的正义事业,你决定去拯救他。
关正义之士的小黑屋迅速被你打开,可是正义之士却被恶魔用一把锁给锁住了。这把锁包含了N个小锁。只有打开前K-2个锁,且锁上第K-1个锁,才能改变第K个锁的状态(打开或锁上该锁),第1个锁可以任意改变状态,当第1个锁锁上时第2个锁就可以改变状态。
为了知道你到底是要留下来开锁,还是“走为上”,你需要知道到底需要多少次操作才能开锁(打开或锁上一把锁算一次操作,只有当N个小锁都被打开进才算开了锁)。

输入save.in
输入文件save.in第一行为一个N(小锁的个数, 1N1000 )。
第二行为n个整数a1,a2,…,an(每个都是0或者1),中间用单个空格隔开。 如果是ai=1,表示第i个锁是锁着的,反之表示该锁已被打开。
输出save.out
输出文件save.out包括一个数,表示最少要操作的次数。

【样例输入】
4
1 0 1 0
【样例输出】
6
【样例说明】

1010111001100100110010000000

【数据规模】

对于40%的数据,有 N30

对于100%的数据,有 N1000 ;

【题解】

这个题目我并没有严格的证明。思路如下

用数学归纳法我们可以知道将数串000…01(一共i个数,前i-1个数都是0,第i个数是1)最后变换成i个0所需要的次数是 2i11

f[i] 表示把前i个数都变成0所需要的步数, h[i] 表示把前i个数变成 00001 (i-1个0,最后一个是1)的最小次数;

那么

a[i]=1 h[i]=f[i1] f[i]=h[i1]+2i11+1

a[i]=0 h[i]=h[i1]+2i11+1 f[i]=f[i1]

以上方程的目标为 f[n]

然后你可以轻松拿到40分,为何不能AC,再看数据你会惊奇的发现 h[i] 肯定会爆掉, 21000 用ULL也存不下,只有高精了。

【代码】

//高精度是复制模板的
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 4000;
const int size = 1000+10;
struct BIGNUM {
    int len,s[MAXN];
    BIGNUM () {
    memset(s,0,sizeof(s));
    len=1;
    }
    BIGNUM operator = (const char* num) {
    len=strlen(num);
    for (int i=0; i<len; ++i) s[i]=num[len-i-1]-'0';
    return *this;
    }
    BIGNUM operator = (const int num) {
    char a[MAXN];
    sprintf(a,"%d",num);
    *this = a;
    return *this;
    }
    BIGNUM (const int num) {
    *this=num;
    }
    BIGNUM (const char * num) {
    *this=num;
    }
    BIGNUM operator + (const BIGNUM & a) {
    BIGNUM c;
    c.len=max(len,a.len)+1;
    for (int i=0,x=0; i<c.len; ++i) {
        c.s[i]=s[i]+a.s[i]+x;
        x=c.s[i]/10;
        c.s[i]=c.s[i]%10;
    }
    if (c.s[c.len-1]==0) --c.len;
    return c;
    }
    BIGNUM operator += (const BIGNUM & a) {
    *this = *this+a;
    return *this;

    }
    BIGNUM operator * (const BIGNUM & x) {
    BIGNUM c;
    c.len=len+x.len;
    for (int i=0; i<len; ++i) {
        for (int j=0; j<x.len; ++j) {
        c.s[i+j]+=s[i]*x.s[j];
        c.s[i+j+1]+=c.s[i+j]/10;
        c.s[i+j]%=10;
        }
    }
    if (c.s[c.len-1]==0) --c.len;
    return c;
    }
    BIGNUM operator *= (const BIGNUM & a) {
    *this = *this * a;
    return *this;

    }
    bool operator < (const BIGNUM & x) const {
    if (len != x.len) return len<x.len;
    for (int i=len-1; i>=0; --i) {
        if (s[i] != x.s[i]) return s[i]<x.s[i];
    }
    return false;
    }
    bool operator > (const BIGNUM & x) const {
    return x<*this;
    }
    bool operator <= (const BIGNUM & x) const {
    return !(x<*this);
    }
    bool operator >= (const BIGNUM & x) const {
    return !(*this<x);
    }
    bool operator == (const BIGNUM & x) const {
    return !(x<*this||*this<x);
    }
    bool operator != (const BIGNUM & x) const {
    return x<*this||*this<x;
    }
}f[size],h[size],mi[size];
ostream& operator << (ostream &out,const BIGNUM& x) {
    for (int i=x.len-1; i>=0; --i)
    cout<<x.s[i];
    return out;
}
istream& operator >> (istream &in,BIGNUM &x) {
    char num[MAXN];
    in>>num;
    x=num;
    return in;
}
LL n,a[size];

int main() {
    freopen("save.in","r",stdin);
    freopen("save.out","w",stdout);
    scanf("%lld",&n);
    mi[0]=1;
    for(int i=1;i<=n;i++)
    mi[i]=mi[i-1]*2;
    for(int i=1;i<=n;i++)
    scanf("%lld",&a[i]);
    if(a[1]==0) {
    f[1]=0;h[1]=1;
    }
    else {
    f[1]=1;h[1]=0;
    }

    for(int i=1;i<=n;i++)
    if(a[i]==1) {
        h[i]=f[i-1];
        f[i]=h[i-1]+mi[i-1];//f[i]=h[i-1]+1+mi[i-1]-1;
    }
        else {
        h[i]=h[i-1]+mi[i-1];//h[i]=h[i-1]+1+mi[i-1]-1;
        f[i]=f[i-1];
    }
    cout << f[n];
    return 0;
}
//抨击这题还要打高精度!

3、魔法物品magic.pas/c/cpp

【问题描述】

有两种类型的物品:普通物品和魔法物品。普通物品没有魔法属性而魔法物品拥有一些魔法属性。每种普通物品有一个价值P,但每种魔法物品有两种价值:鉴定前的价值P1和鉴定后的价值P2(当然,P2总是大于P1)。

为了鉴定一个魔法物品,你需要购买一个鉴定卷轴,用它来鉴定魔法物品。鉴定完一件魔法物品以后,鉴定卷轴便会消失。每个鉴定将会消耗Pi元钱,如果没有足够的钱,你将无法购买任何鉴定卷轴。

现在,你正在一个集市中,同时拥有很多物品。你知道每件物品的价值并且想要出售全部物品。那么,你最多能够获得多少钱呢?

你可以假定:
• 开始的时候你没有钱。
• 所有的魔法物品都还没有被鉴定。
• 只要你有足够的钱,你可以购买任意多的鉴定卷轴。

输入magic.in

第一行有两个整数N和Pi ( 0<Pi5000 ),表示你拥有的物品数和一个鉴定卷轴价格。
接下来N行,每行给出一件物品的价格。
对于每件普通物品,那一行仅有一个整数P ( 0<P10000 )。
对于每件魔法物品,那一行将会有两个整数P1和P2 ( 0<P1<P210000 )

输出magic.out
一个整数表示你最多能够获得多少钱。

【样例输入】
2 10
10
20 100
【样例输出】
100
【题解】
说实话刚开始以为是贪心,后来觉得贪心有点不靠谱,往动归方面去想,然后就…MLE了。

其实可以这样去想啊

首先显然如果全部是普通物品或者 p2p1p 时这些物品显然可以卖掉(某人:不然还可以吃掉啊)

卖掉了这些之后我们手里有了一些Money,这个时候我们可以来判断,若这些Money足够买一个卷轴的话,我们显然可以在每卖下一个物品之前利用手中的钱买一个卷轴,然后作鉴定,这样做一定是获益最大的。(还是有点贪心的成分在吧)

那么怎么说上面这段话是正确的呢?

首先,这些剩下的物品满足的特征一定是 P2P1>P ,其实就是这样说吧,先买一个卷轴将它鉴定,然后再卖出去一定是赚钱的;其次,你会发现由于 P2P1>P ,所以 P2 一定大于 P ,其实,你若按照鉴定完了的价钱卖出的话,你一定有足够的钱再买一个卷轴,然后依次类推,你一定可以买卷轴卖物品买卷轴卖物品一直到卖掉所有的物品。这样的情况是最简单的,就是模拟一下买卷轴和买物品就行了。

可是可是可是,若这些钱买不起卷轴,那么我们就要按照鉴定前的价格卖出一些物品,让我们有钱够买卷轴,之后问题就转化成了上面的第一种情况。

但是用哪些物品来凑钱呢?我们定义一个物品的亏损=(P2P)P1(由 P2P1>P 可知这个式子一定大于0,其含义为使它鉴定的收益和不使它鉴定直接卖出的收益之差),我们要选出一些物品,条件是使这些物品按照原价卖出后加上原来手里有的钱足够买一个卷轴,且总亏损最小。

那么就可以轻松用背包来解。 f[v] 表示将物品恰好放入体积为v的背包里的最小亏损,然后枚举一下满足条件的体积即可

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int size = 1000+10;
const int maxn = 10000000+10;
int n,p,x,y,money,tmp,k,m,ans;
int f[maxn];
struct node{
    int p1,p2,cnt;
}a[size];

int main() {
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    scanf("%d%d",&n,&p);
    for(int i=1;i<=n;i++) {
    scanf("%d",&x);
    char ch=getchar();
    if(ch!=' ') money+=x;
    else {
        scanf("%d",&y);
        if(y-x<=p) money+=x;
        else {
        a[++tmp].p1=x; a[tmp].p2=y;
        a[tmp].cnt=y-x-p;
        m+=x; k+=y;
        }
    }
    }
    if(money>=p) {
    money=money-p*tmp+k;
    return printf("%d\n",money),0;
    }
    memset(f,127/3,sizeof(f));
    f[0]=0;
    for(int i=1;i<=tmp;i++)
    for(int v=m;v>=a[i].p1;v--) {
        f[v]=min(f[v],f[v-a[i].p1]+a[i].cnt);
        if(v>=p-money&&f[v]!=707406378)//707406378=127/3
        ans = max(ans,money+k-(f[v]+p*tmp));
    }
    if(ans==0) ans = money+m;
    printf("%d\n",ans);
    return 0;
}

4、邮递员carrier.pas/c/cpp

【题目描述】

邮局需要你来帮助他们为某个邮递员设计出一条能够穿过那遥远乡村的所有村子和小路至少一次的邮路(输入数据将会保证这么一条路是一定存在的)。
但是,每条路线都是有一个花费的。各个村子里的村民希望邮递员到达他们村子的时间越早越好。因此,各个村子里的人们采用了一些措施:假设第i号村子是邮递员在他的邮递路线上到达的第k个村子。如果 kw(i) ,那么这个村子的村民就会付给邮局 w(i)k 欧元。当然,如果 k>w(i) ,邮局也同意付 kw(i) 欧元给这个村子,对某些村子重复经过要重复收费。此外,邮递员每经过一条小路,邮局也要付给邮递员1欧元作为补贴。
现在有n个村子,编号依次为1到n。邮局就位于1号村子,因此邮递员的传递路线从这里开始,也从这个村子结束。能够离开每个村子的路口的数目一定是2,4或者8。这里允许出现同样的村子间存在多条小路,或者某条小路构成了一个自环的情况。
你的任务是设计一个路线使得邮局赚的钱最多(或者说赔的钱最少。如果有多种最优解,输出字典序最小的。
输入carrier.in
第一行:两个整数n,m,分别表示村子的数量和小路的数量。
接下来n行,每行一个整数: w(i)(1w(i)<1000)
接下来m行,每行两个整数u,v,表示这条小路连接的村子的编号。
输出carrier.out
第一行:一个整数k,你的程序所设计的路径的长度
第二行:k+1个整数,v1,v2…vk+l,每个数之间用一个空格隔开,表示你设计的路径所经过的村子的编号,其中需要满足 v1=vk+1=1

【样例输入】
6 7
1
7
4
10
20
5
2 4
l 5
2 l
4 5
3 6
1 6
l 3
【样例输出】
7
1 5 4 2 1 6 3 1
【数据规模】

对于30%的数据,有 N20
对于100%的数据,有 N200 ;

【样例解释】

邮递员每条路线都要去送信,并且每条线路只要送一次就可以了。同时输出要求字典序最小,但样例输出时只给了一个可行解,没有字典序最小。
hint

【题解】

这个题目我是乱搞出来的,其实也不知道怎么做。总之是数据水吧还是怎么,我连w[i]的值用都没用,却也可以A掉这个题,想象一下吧。

其实就是找欧拉回路。

【代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int size = 200+10;
int n, m,tot;
int a[size],w[size][size],sta[size];

void dfs(int x);

int main() {
    freopen("carrier.in","r",stdin);
    freopen("carrier.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    for(int i=1,x,y;i<=m;i++) {
    scanf("%d%d",&x,&y);
    w[x][y]++;w[y][x]++;
    }
    dfs(1);
    printf("%d\n",tot-1);
    for(int i=tot;i>=1;i--)
    printf("%d ",sta[i]);
    putchar(10);
    return 0;
}

void dfs(int x) {
    for(int i=1;i<=n;i++)
    if(w[x][i]>0) {
        w[x][i]--;w[i][x]--;
        dfs(i);
    }
    tot++;
    sta[tot]=x;
}

总结

这次考试总体还好,但还有许多的提升空间,还是得继续稳基础,不得太浮了。

排名名称carriermagicofficersave总分
2zyg100010040240

可怜了我的magic…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: C test9.6下载是一个指的是在C编程语言中进行测试和实验的软件或文件的下载。C语言是一种非常流行的编程语言,被广泛应用于软件开发和系统编程。而C test9.6下载即为C语言的一个测试版本或实验版本的下载。 C test9.6下载可能包括以下内容:包含了C语言最新版本的编译器和相关的开发工具,用于编写、编译和运行C程序;提供了C语言相关的库文件和头文件,用于程序开发中常用的函数和数据类型的定义;可能还包括了示例代码和教程,帮助用户学习和使用C语言。 通过C test9.6下载,用户可以在自己的计算机上进行C语言程序的开发和测试。他们可以使用提供的编译器将写好的C代码编译成可执行文件,然后运行和测试这些程序,检查其功能和效果。用户还可以通过阅读示例代码和教程,学习和了解C语言的各种特性和用法。 C test9.6下载对于那些学习和使用C语言的开发者来说是非常有用的。他们可以通过这个软件来提高自己的C语言编程技能,并且可以在实际项目中应用这些技能。而且,C test9.6下载也可以帮助开发者更好地理解和掌握C语言的各种概念和技术,为他们今后的学习和实践奠定基础。 总之,C test9.6下载是一个用于下载C语言测试版本或实验版本的软件或文件,可以帮助开发者进行C语言程序的开发、测试和学习。它是C语言编程学习和实践的重要工具,对于提高编程技能和解决实际问题非常有帮助。 ### 回答2: C test9.6下载是一款C语言测试题的软件。C语言是一种通用的、面向过程的计算机编程语言,广泛应用于计算机科学和软件开发领域。C test9.6下载提供了相应的测试题目,供学习者或从事C语言编程的人士进行练习和测试。 通过C test9.6下载,用户可以在自己的电脑上进行C语言知识的检验与学习。用户可以通过试题的形式来检测自己对C语言的掌握程度,以此来提高自己对C语言的理解和应用能力。这个软件可能包含了基础的C语言知识点、算法题、编程题等,通过解答这些题目,可以培养用户的编程思维和解决问题的能力。 C test9.6下载可以帮助用户提高自己的C语言能力,提供了一个学习和练习的平台。通过反复的练习和测试,用户可以逐渐提高自己的编程技能,并在实际应用中更加熟练地运用C语言来解决问题。 总之,C test9.6下载是一个有助于学习和练习C语言的软件。通过下载安装该软件,用户可以进行C语言的测试题练习,提高自己的编程能力,进一步提升在计算机编程领域的竞争力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值