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里下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM