XTU 2016上学期《程序设计实践》练习-1 题解

Permutation

Description

置换
题目描述

给一个置换P(x⃗ ),Pn(x⃗ )=x⃗ ,求最小的n。比如置换P=(12233441),
P(x1,x2,x3,x4)P2(x1,x2,x3,x4)P3(x1,x2,x3,x4)P4(x1,x2,x3,x4)====(x4,x1,x2,x3)(x3,x4,x1,x2)(x2,x3,x4,x1)(x1,x2,x3,x4)
所以n=4。

输入

第一行是一个整数K(1≤K≤1000),表示样例的个数。
每个样例占一行,第一个整数是n(1≤n≤100),以后的n个整数xi,1≤xi≤n且 xi是唯一的, (1x12x2⋯⋯nxn)表示一个置换。

输出

输出一个样例的结果。

样例输入

3
3 1 2 3
3 2 1 3
3 2 3 1
样例输出

1
2
3

解题思路:

这个题只要清楚置换的具体操作即可(实在不明白,就去看看离散书吧)。

首先对于置换中的任意一个元素,我们通过重复执行给定的置换使其回到自己位置并记每个元素回到自己位置需要的置换次数。然后求这些数字的最小公倍数即可。

注意:该题数据会超出int,需要使用long long或__int64来存储数据。

/*这题就是把每个置换到自身需要次数的最小公倍数求出来,置换如何弄请参考离散数学,注意int存不下*/
#include<bits/stdc++.h>

using namespace std;

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

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        int a[111];//如果这里在外面声明请初始化
        for(int i = 1 ;i <= n;i++)scanf("%d",&a[i]);
        __int64 res = 1;
        for(int i = 1 ;i <= n;i++){
            __int64 tmp = 1;
            int pos = a[i];
            while(pos!=i){
                tmp++;
                pos = a[pos];
            }//求第i个元素需要置换几次
            //printf("i = %d,tmp = %d\n",i,tmp);
            res = res * tmp /gcd(res,tmp);
        }
        printf("%d\n",res);
    }
    return 0;
}

汽水瓶
题目描述

小明非常喜欢喝汽水,家门口的超市今天搞活动,2个汽水空瓶或者4个瓶盖可以换1瓶汽水,小明现在已经买了n瓶汽水,请问通过这个活动可以多得到多少瓶汽水?

输入

每行输入一个整数n(0≤n≤108),如果n=0,表示输入结束,这个样例不需要处理。

输出

每行输出一个结果。

样例输入

1
5
0
样例输出

0
10
解题思路:

没什么难点,读懂题就可以了。不断的对瓶数/2,然后更新。瓶盖同理。直到两个都不能再更新为止。模拟题。

#include<bits/stdc++.h>

using namespace std;

int main(){
    int n;
    while(scanf("%d",&n)&&n){
        __int64 res = 0;
        int bo = n;
        int ba = n;
        while(bo>=2||ba>=4){
            while(bo>=2){
                res += bo/2;
                ba  += bo/2;
                bo  -= bo/2;
            }
            while(ba>=4){
                res += ba/4;
                bo  += ba/4;
                ba  -= ba/4*3;
            }

        }
        printf("%I64d\n",res);
    }
}

Robb's Problem
题目描述

Robb想知道阶乘n!第m位数码是什么?

输入

第一行是一个整数T,(1≤T≤10000)
每行一个样例,为2个整数n,m,0≤n≤1000,1≤m≤log10n!+1

输出

每行输出一个样例的结果

样例输入

3
5 1
5 2
5 3
样例输出

0
2
1

解题思路:
这题其实关键是大数,可以选择用java写,可以过。但是c++可以用万位进制来实现模拟。

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1111;
int res[maxn][maxn];

int print(int n,int m,int i){
    int tmp = res[n][m];
    for(int j = 0;j < i-1;j++)tmp/=10;
    printf("%d\n",tmp%10);
}
int main(){
    memset(res,0,sizeof(res));
    res[0][0] = 1;
    int n = 1;
    for(int i = 1;i < 1001;i++){
        int tmp = 0 ;
        for(int j = 0;j < n ;j++){
            res[i][j]  = res[i-1][j] * i;
            res[i][j] += tmp;
            tmp = res[i][j] /100000;
            res[i][j] %= 100000;
            if(tmp != 0&&j==n-1)n++;
        }
    }
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        print(n,(m-1)/5,m%5?m%5:5);
    }
    return 0;
}

Matrix Transposition
题目描述

矩阵转置就是把原矩阵A的所有元素aij转成矩阵B的bji。 现实中,大部分的矩阵都是稀疏的,所以,存储矩阵时,我们可以只存储存储每个非零元素的坐标和值,并且按行优先排序。 比如说3×3矩阵

0 1 0
0 2 3
0 0 0
其转置矩阵为

0 0 0
1 2 0
0 3 0
上面矩阵使用稀疏矩阵的存储方法存(i,j,aij)为

0 1 1
1 1 2
1 2 3
其转置矩阵

1 0 1
1 1 2
2 1 3
输入

第一行是一个整数T,(0<T≤10),表示样例的数目。
每个样例的第一行是三个整数N,M,K,1≤N,M≤1000,1≤K≤10000,分别表示矩阵的行数,列数,非零元素个数。
以后的K行,每行三个整数X,Y,V,0≤X<N,0≤Y<M,−100≤V≤100,表示元素的行,列,值。
数据保证输入元素的顺序按行优先有序。

输出

输出每个样例的结果,每个样例输出之后有一个空行。

样例输入

2
3 3 3
0 1 1
1 1 2
1 2 3
1 3 1
0 0 1
样例输出

1 0 1
1 1 2
2 1 3

0 0 1
解题思路:

定义一个结构体,分别存储x,y,v表示第x行,第y列的值为v。然后按y升序排序,输出y,x,v的转置邻接表即可。

排序时推荐使用sort函数(忘记qsort的存在吧),至于比较函数可以自己写cmp函数,也可以重载运算符<<,看个人喜好吧。

#include<bits/stdc++.h>

using namespace std;
struct Node{
    int x,y,z;
}a[10010];

int cmp(Node c,Node d){
    if(c.y==c.y)return c.x<c.x;
    return c.y<d.y;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int x,y,n;
        scanf("%d%d%d",&y,&x,&n);
        for(int i= 0;i < n;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        sort(a,a+n,cmp);
        //printf("========\n");
        for(int i= 0;i < n;i++)printf("%d %d %d\n",a[i].y,a[i].x,a[i].z);
        printf("\n");
    }
    return 0;
}

Lisa's Puzzle
题目描述

5的二进制是101,13的二进制是1101,所以在二进制上,5是13的后缀。Lisa获得了一个长长的正整数列表,她想知道在列表中每一个数是列表中多少个其他数的后缀?

输入

第一行是一个整数N,1≤N≤100000,表示整数的个数。 以后N行,每行一个正整数,每个都可以使用一个32位int表示,而且所有的数都是唯一的。

输出

每个整数对应的结果输出一行,

样例输入

5
5
13
1
2
3
样例输出

1
0
3
0
0
解题思路:

这道题的话字典树其实很好理解,你可以把它想象成一个完全二叉树,除了根节点外每个结点都有值0/1,每个结点的左孩子表示0,又孩子表示1。对于一个给定的数,我们从根节点出发依次按照数的低位到高位在二叉树上一层一层的走下去,对于过程中经过的每一个点我们将他的计数+1(初始为0)。这样,当我们把所有的数都在这一颗二叉树上处理下来。只要输出这个最高位所对应结点里的计数值即可。不过我还选择无脑字典树...

/*Âã×ÖµäÊ÷*/
#include<bits/stdc++.h>

using namespace std;
const int maxnode = 100005*32;
const int si = 2;

struct trie{
    int ch[maxnode][si];
    int num[maxnode];
    int sz;
    void clear(){
        sz = 1;
        memset(ch[0],0,sizeof(ch[0]));
        memset(num,0,sizeof(num));
    }
    void insert(int x){
        int u = 0;
        int n =32;
        for(int i = 0;i < n&&x;i++){
            if(!ch[u][x%2]){
                memset(ch[sz],0,sizeof(ch[sz]));
                num[sz] = 0;
                ch[u][x%2] = sz++;
            }
            u = ch[u][x%2];
            num[u]++;
            x/=2;
        }
    }
    int find(int x){
        int u = 0;
        int n = 32;
        for(int i = 0;i < 32&&x;i++){
            u = ch[u][x%2];
            x /= 2;
        }
        return num[u];
    }
};
trie t;
int arr[maxnode/32];

int main(){
    int n;
    while(~scanf("%d",&n)){
        t.clear();
        for(int i = 0;i < n;i++){
            scanf("%d",&arr[i]);
            t.insert(arr[i]);
        }
        for(int i = 0;i < n;i++){
            printf("%d\n",t.find(arr[i])-1);
        }

    }
    return 0 ;
}

Estrella's Chocolate
题目描述

Estrella是个漂亮的小姑娘,她最喜欢吃的零食就是巧克力,但是巧克力吃多了会发胖,美貌和美食之间她必须做出艰难地选择。
Estrella有N颗巧克力,她按照喜欢的程序给巧克力排好序,并决定在M天内吃完这些巧克力。由于一颗巧克力如果不一次吃完,味道就会变坏,所以她绝对不会把一颗巧克力分开吃。但是每颗巧克力的热量并不相同,Estralla希望知道M天中每天吃的巧克力热量总和最大值。为了尽可能防止发胖,她希望这个值越小越好,请问这个值最小是多少?

输入

第一行是一个整数T(1≤T≤100),表示样例的个数。
每个样例的第一行是两个整数N,M(1≤M≤N≤10000)。
每个样例的第二行是N个整数,表示每颗巧克力的热量,其值处于[1,100000] 之间。

输出

每行输出一个样例的结果。

样例输入

2
5 2
5 3 2 4 1
5 3
5 3 2 4 1
样例输出

8
5
解题思路:
首先关注以下几点:
巧克力要全部吃完
一块巧克力不能分开吃。
要按照给定的顺序吃。
因此,设第i块巧克力的热量为cici。我们可以知道最终答案的范围为[max(ci),∑n1(ci)][max(ci),∑1n(ci)]。这样的话我们采用二分查找的办法来确定最后的答案。对于每一个枚举的值,我们只需判断按照给定顺序吃能否在规定天数内吃完。

#include<bits/stdc++.h>

using namespace std;

int n,m;
int a[10000+1];
int check (int ans)
{
    int sum=0;
    int i;
    int i_count=0;
    for (i=1;i<=n ;i++ )//枚举每一个巧克力
    {
        sum += a[i];
        if (sum>ans)
        {
            sum=a[i];//吃不下
            i_count++;
        }
        if (i_count>=m)
        {
            return 0;
        }
    }
    return 1;
}
void work ()
{
    scanf ("%d%d",&n,&m);
    int i;
    int max=-1;
    int sum=0;
    memset (a,0,sizeof(a));
    for (i=1;i<=n ;i++ )
    {
        scanf ("%d",&a[i]);
        if (max<a[i])
        {
            max = a[i];
        }
        sum += a[i];
    }
    int begin=max;
    int end = sum;
    int ans=9999999;
    while (begin<=end)
    {
        int mid=(begin+end)/2;
        if (check(mid))
        {
            if (ans>mid)
            {
                ans=mid;
            }
            end=mid-1;
        }
        else
        {
            begin=mid+1;
        }
    }
    printf ("%d\n",ans);
    return ;
}
int main ()
{
    int t;
    scanf ("%d",&t);
    while (t--)
    {
        work ();
    }
    return 0;
}

Bob's Password
题目描述

Bob最新购入一款安卓手机,他发现安卓手机密码使用的是画线方式。
一共有9个点,我们按行列顺序依次为1~9。密码要求在点和点之间连线不能有还未曾经过的点。
比如说:从1出发,可以到2,4,5,6,7,8,但是不能到达3,7,9。
但是如果从2出发,先到1,这时因为2已经经过了,所以此时可以到达3。
现在给你一个密码,请问它是否符合密码的这个要求?

输入

第一行是一个整数T(1≤T≤10000),表示样例的个数。
一个样例占一行,是一个最短为4位,最长9位,只含1-9的字符串,且1-9最多只有1个。

输出

每个样例输出一行,如果合法输出“Yes”,否则输出“No”。

样例输入

3
16852
213
132
样例输出

Yes
Yes
No

解题思路:
首先暴力枚举a[9][9].a[i][j]为1表示i可以直接到j。当然这题如果i和j不能直接到达就得通过中间的点来过去,这时特判下。其实还是一道暴力模拟题...

#include<bits/stdc++.h>

using namespace std;

int main(){
    int t;
    cin>>t;
    int a[9][9]={0,1,0,1,1,1,0,1,0,
                     1,0,1,1,1,1,1,0,1,
                     0,1,0,1,1,1,0,1,0,
                     1,1,1,0,1,0,1,1,1,
                     1,1,1,1,0,1,1,1,1,
                     1,1,1,0,1,0,1,1,1,
                     0,1,0,1,1,1,0,1,0,
                     1,0,1,1,1,1,1,0,1,
                     0,1,0,1,1,1,0,1,0};
    int b[100];
    while(t--){
        string s;
        cin>>s;
        bool flag = true;
        int len = s.length();
        memset(b,0,sizeof(b));
        for(int i = 0;i < len-1;i++){
            if(a[s[i]-'1'][s[i+1]-'1']==1){
                b[s[i]-'1'] = 1;
            }else{
                if(b[(s[i]+s[i+1]-2*'1')/2]==1){
                    b[s[i]-'1'] = 1;
                }else{
                    flag = false;break;
                }
            }
        }
        flag?puts("Yes"):puts("No");
    }
    return 0;
}

Alice's Key
题目描述

Alice经常弄丢钥匙,所以她经常需要去配钥匙,但是锁匠不能保证每一把配的钥匙都能打开。Alice 不想多跑,所以她决定一次让锁匠配多把钥匙来提高成功率。假设每次配钥匙都是独立事件,锁匠有p100的概率配好钥匙,请问Alice要达到r100的概率至少有一把钥匙能打开门,最少需要配多少把钥匙?

输入

第一行是一个整数T(0≤T≤11,000),表示样例数。 每个样例占一行,为两个整数p,r,0≤p,r≤100。

输出

每行输出一个样例的结果。如果不可能达到目的,输出“Impossible”(不要输出引号),否则输出一个整数。

样例输入

50 90
样例输出

4

解题思路:
简单的概率题,注意一下三种情况需要特判:
爱丽丝希望钥匙概率为100%,但钥匙的概率不是100%
爱丽丝希望钥匙概率为0%,但钥匙的概率不是0%
爱丽丝希望钥匙概率不为0%,但钥匙的概率是0%
其他的输出计算的结果就行了。
提示:涉及到浮点数比较,但浮点数本身存在误差,故判断两个浮点数是否相等应以其差的绝对值与EPS作比。

#include <bits/stdc++.h>
using namespace std;
const double EPS = 1e-12;
int main() {
    int p, r, t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &p, &r);
        if((p == 0 && r != 0) || (p != 100 && r == 100) || (p != 0 && r == 0)) puts("Impossible");
        else {
            int cnt = 1;
            double n = p/100.0, m = r/100.0;
            double s = n;
            while(s < m) {
                if(fabs(s - m) < EPS) break;
                s += (1-s) * n;
                cnt++;
            }
            printf("%d\n", cnt);
        }
    }
}

该博客所有代码都可在:http://pan.baidu.com/s/1mi4YiEG里下载

posted @ 2017-03-23 21:56  adfae  阅读(4115)  评论(0编辑  收藏  举报