1. 程式人生 > >演算法課2-演算法課第一次作業總結

演算法課2-演算法課第一次作業總結

A:單詞翻轉 總時間限制: 1000ms 記憶體限制: 65536kB 描述: 輸入一個句子(一行),將句子中的每一個單詞翻轉後輸出。

輸入: 只有一行,為一個字串,不超過500個字元。單詞之間以空格隔開。 輸出: 翻轉每一個單詞後的字串,單詞之間的空格需與原文一致。 樣例輸入

hello world

樣例輸出

olleh dlrow

這道題算是首次用c++在oj上刷題,一開始呼叫了reverse函式,一直WA,很鬱悶,直接cout倒是AC了。

#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string> #include <cstdio> #include <algorithm> using namespace std; int main() { string sentence; getline(cin,sentence);//使用了getline int left=0;//單詞左邊 int right=0;//單詞右邊 while(right<sentence.size()){ //遇到空格就是一個單詞(中間多個空格也成立) if(sentence[right]==
' ') { for(int i=right-1;i>=left;i--) { cout << sentence[i]; } cout<<' '; left=right+1; } if(right==sentence.size()-1){ for(int i=right;i>=left;i--) { cout << sentence[
i]; } } right++; } return 0; }

B:密碼 總時間限制: 1000ms 記憶體限制: 65536kB 描述 Bob 和 Alice 開始使用一種全新的編碼系統。它是一種基於一組私有鑰匙的。他們選擇了n個不同的數a1 , . . .,an, 它們都大於0小於等於n。 機密過程如下:待加密的資訊放置在這組加金鑰匙下,資訊中的字元和金鑰中的數字一一對應起來。資訊中位於i位置的字母將被寫到加密資訊的第ai個位置, ai 是位於i位置的金鑰。加密資訊如此反覆加密,一共加密 k 次。

資訊長度小於等於n。如果資訊比 n 短, 後面的位置用空格填補直到資訊長度為n。

請你幫助 Alice 和 Bob 寫一個程式,讀入金鑰,然後讀入加密次數 k 和要加密的資訊,按加密規則將資訊加密。

輸入 輸入包括幾塊。每塊第一行有一個數字n, 0 < n <= 200. 接下來的行包含n個不同的數字。數字都是大於0小於等於n的。下面每行包含一個k和一個資訊字串,它們之間用空格格開。每行以換行符結束,換行符不是要加密的資訊。每個塊的最後一行只有一個0。 最後一個塊後有一行,該行只有一個0。 輸出 輸出有多個塊,每個塊對應一個輸入塊。每個塊包含輸入中的資訊經過加密後的字串,順序與輸入順序相同。所有加密後的字串的長度都是 n。 每一個塊後有一個空行。 樣例輸入

10 4 5 3 7 2 8 1 6 10 9 1 Hello Bob 1995 CERC 0 0

樣例輸出

BolHeol b C RCE

來源 poj 1026

這道題的輸入就難倒我了,結果還不是表面上看起來的容易,需要找到密碼的迴圈節,也就是說,找到轉換多少次後又對應位置的字母又回到了原位,不然會超時。

#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>


using namespace std;

int main()
{
    int n,k;
    int code[210],T[210],ans[210];
    bool vis[210];//類似union find
    char s[210];
    while(cin>>n)
    {
        if(n==0) break;
        for(int i=1;i<=n;++i)
        {
            cin>>code[i];
        }
        memset(vis,0,sizeof(vis));//陣列初始化
        for(int i=1;i<=n;++i)
        {
            if(vis[i])continue;
            vis[i]=true;
            int cnt=1;
            for(int j=code[i];j!=i;j=code[j])
            {
                cnt++;
                vis[j]=true;
            }
            T[i]=cnt;//找到圈,存下來
            for(int j=code[i];j!=i;j=code[j])
            {
                T[j]=cnt;//所有的這條路徑,肯定都是再變換這麼多次就回來
            }
        }
        while(cin>>k)
        {
            if(k==0) break;
            getchar();//防空格
            cin.getline(s+1,200);
            int len=strlen(s+1);
            for(int i=len+1;i<=n;++i)
            {
                s[i]=' ';//末尾填充空格
            }
            s[n+1]='\0';//末尾填充字串結尾符
            for(int i=1;i<=n;++i)
            {
                int j=i;
                for(int t=0;t<(k%T[i]);++t)//變換k%T[i]次即可
                {
                    j=code[j];
                }
                ans[j]=i;
            }
            
            for(int i=1;i<=n;++i)
            {
                cout<<s[ans[i]];
            }
            cout<<endl;
        }
        cout<<endl;
    }
    return 0;
}

C:陣列轉換

總時間限制: 1000ms 記憶體限制: 65535kB 描述 有n個數字1,2,3…,n,由小至大依次放入棧。問是否存在一種出棧方式,使這n個數字出棧的順序為a1,a2,…,an。

輸入 第一行輸入一個整數t,代表有t組測試資料 對於每組測試資料,第一行輸入整數n,第二行輸入n個數字:a1,a2, … , an 1<= n <= 1000 輸出 每組測試資料輸出一行。 如果出棧順序a1,a2,…,an是合法的,輸出yes,否則輸出no。 樣例輸入

3 3 1 2 3 3 3 2 1 4 1 4 2 3

樣例輸出

yes yes no

這道題的思路是說,找一個棧直接模擬這個入棧出棧的過程,如果放進去要彈出來,就得先把前面壓進去的彈出來。我一開始的思路不太對,想著先找出那些立馬彈出來的(以為下標等於數字就行),再根據棧的彈出過程來判斷,這樣子沒有考慮全面,比如1342也可以。

#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;

int main() {
    int t;
    cin>>t;
    int test_num=0;
    while(test_num<t){
        int n;
        cin>>n;
        stack<int>s;
        int nums[n];
        for(int i=0;i<n;i++){
            int num;
            cin>>num;
            nums[i]=num;
        }
        int point=0;
        bool flag=true;
        for(int i=0;i<n;i++)
        {
         //將nums[i]前面的元素都壓入棧
         //point儲存壓到哪個元素了
            for(int j=point+1;j<=nums[i];j++)
            {
                point=j;
                s.push(j);
            }
            //不等於就不對了
            if(s.top()!=nums[i]) {
                flag = false;
                break;
            }
            else {
                s.pop();
            }
        }

        if(flag) {
            cout << "yes" << endl;
        }
        else{
            cout<<"no"<<endl;
        }
        test_num++;

    }
    return 0;
}

D:漢諾塔問題(Hanoi)

總時間限制: 1000ms 記憶體限制: 65535kB 描述 一、漢諾塔問題

有三根杆子A,B,C。A杆上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至C杆: 每次只能移動一個圓盤; 大盤不能疊在小盤上面。 提示:可將圓盤臨時置於B杆,也可將從A杆移出的圓盤重新移回A杆,但都必須遵循上述兩條規則。

問:如何移?最少要移動多少次?

輸入 輸入為一個整數後面跟三個單字元字串。 整數為盤子的數目,後三個字元表示三個杆子的編號。 輸出 輸出每一步移動盤子的記錄。一次移動一行。 每次移動的記錄為例如3:a->b 的形式,即把編號為3的盤子從a杆移至b杆。 我們約定圓盤從小到大編號為1, 2, …n。即最上面那個最小的圓盤編號為1,最下面最大的圓盤編號為n。 樣例輸入

3 a b c

樣例輸出

1:a->c 2:a->b 1:c->b 3:a->c 1:b->a 2:b->c 1:a->c

主要參考了上面寫的網址,使用遞迴,總體步驟就是,先把n-1個盤子藉助第3個柱子挪到第2個柱子,再把這個盤子挪到第3個柱子,然後再把n-1個盤藉助第一個盤挪到第三個盤。

#include<cstdio>
#include<cstring>
#include<vector>
#include<utility>
#include <iostream>
using namespace std;

int move(int num,char from,char temp,char to){
	//遞迴的邊界條件,盤子只剩一個
    if(num==1){
        cout<<num<<":"<<from<<"->"<<to<<endl;
    }
    else{
        move(num-1,from,to,temp);
        cout<<num<<":"<<from<<"->"<<to<<endl;
        move(num-1,temp,from,to);
    }
}



int main(){
    int n;
    cin>>n;
    char a,b,c;
    cin>>a>>b>>c;
    move(n,a,b,c);
    return 0;
}

E:由中根序列和後根序列重建二叉樹

總時間限制: 500ms 記憶體限制: 65535kB 描述 我們知道如何按照三種深度優先次序來周遊一棵二叉樹,來得到中根序列、前根序列和後根序列。反過來,如果給定二叉樹的中根序列和後根序列,或者給定中根序列和前根序列,可以重建一二叉樹。本題輸入一棵二叉樹的中根序列和後根序列,要求在記憶體中重建二叉樹,最後輸出這棵二叉樹的前根序列。

用不同的整數來唯一標識二叉樹的每一個結點,下面的二叉樹

中根序列是9 5 32 67

後根序列9 32 67 5

前根序列5 9 67 32

輸入 兩行。第一行是二叉樹的中根序列,第二行是後根序列。每個數字表示的結點之間用空格隔開。結點數字範圍0~65535。暫不必考慮不合理的輸入資料。 輸出 一行。由輸入中的中根序列和後根序列重建的二叉樹的前根序列。每個數字表示的結點之間用空格隔開。 樣例輸入

9 5 32 67 9 32 67 5

樣例輸出

5 9 67 32

碰到樹的問題首先想遞迴,我的思路是從後序遍歷中先找到根(最後一個元素),再從確定中序遍歷的左右子樹內容,對左右子樹遞迴。

#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;



void rebuild(int mid[],int back[],int l1,int r1, int l2,int r2){
	//輸出根節點
    cout<<back[r2]<<" ";
    //停止條件,如果只剩一個元素就結束
    if(l1-r1==0)
        return;
     //找到根節點位置
    int point1=-1;
    for(int i=l1;i<=r1;i++){
        if(back[r2]==mid[i]){
            point1=i;
        }
    }
    //有左子樹
    if(point1-1>=0) {
        int l2_new = -1;
        int r2_new = -1;
        for (int i = l2; i <= r2; i++) {
            if (back[i] == mid[l1]) {
                l2_new = i;
            }
            if (back[i] == mid[point1 - 1]) {
                r2_new = i;
            }
        }

        rebuild(mid, back, l1, point1 - 1, l2_new, r2_new);
    }
    //有右子樹
    if(mid[point1+1]>0) {
        int l2_new = -1;
        int r2_new = -1;
        for (int i = l2; i <= r2; i++) {
            if (back[i] == mid[point1 + 1]) {
                l2_new = i;
            }
            if (back[i] == mid[r2]) {
                r2_new = i;
            }

        }
        rebuild(mid, back, point1 + 1, r2, l2_new, r2_new);
    }

}


int main() {
    int i=0;
    int mid[65536];
    int back[65536];
    while(cin>>mid[i++])
    {
        if(cin.get() != ' ') break;
    }

    i=0;
    while(cin>>back[i++])
    {
        if(cin.get() != ' ') break;
    }
   rebuild(mid,back,0,i-1,0,i-1);

    return 0;
}

F:區間合併

總時間限制: 1000ms 記憶體限制: 65536kB 描述 給定 n 個閉區間 [ai; bi],其中i=1,2,…,n。任意兩個相鄰或相交的閉區間可以合併為一個閉區間。例如,[1;2] 和 [2;3] 可以合併為 [1;3],[1;3] 和 [2;4] 可以合併為 [1;4],但是[1;2] 和 [3;4] 不可以合併。

我們的任務是判斷這些區間是否可以最終合併為一個閉區間,如果可以,將這個閉區間輸出,否則輸出no。

輸入 第一行為一個整數n,3 ≤ n ≤ 50000。表示輸入區間的數量。 之後n行,在第i行上(1 ≤ i ≤ n),為兩個整數 ai 和 bi ,整數之間用一個空格分隔,表示區間 [ai; bi](其中 1 ≤ ai ≤ bi ≤ 10000)。 輸出 輸出一行,如果這些區間最終可以合併為一個閉區間,輸出這個閉區間的左右邊界,用單個空格隔開;否則輸出 no。 樣例輸入

5 5 6 1 5 10 10 6 9 8 10

樣例輸出

1 10

一開始就想到要排序,但是c++的結構體不會弄,又學習了一波。

#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

//定義結構體 oj常用
struct area{
    int upper;
    int lower;
};
//定義比較函式
int cmp(area x1, area x2){
    return x1.lower<x2.lower;
}


int main() {
    vector<int> v1;
    vector<int> v2;

    int n;
    cin>>n;
    area a[n];
    int areanum=0;
        while(areanum<n){
            int lower;
            cin>>lower;
            int upper;
            cin>>upper;
            a[areanum].lower=lower;
            a[areanum].upper=upper;
            areanum++;
        }
	//排序用法
    sort(a,a+n,cmp);
    int l=a[0].lower;
    int h=a[0].upper;
    for(int i=0;i<n;i++){
        if(a[i].lower>h){
            cout<<"no";
            return 0;
        }
        else{
            h=max(a[i].upper,h);
        }
    }
    cout<<l<<" "<<h;

    return 0;
}

主要學習oj上常用的c++語法,和一些思路(比如迴圈節),收穫挺大的,繼續努力。