2016年3月5日 星期六

2016.03.05 apcs 程式題 [有樹直徑模板]

這次沒有我想像中那麼簡單XD(           )APCS試題(程式部分)

第一題:

給你一個長度是n的數列(1<=n<=20),數值m的範圍是0<=m<=100,我們定義數值<60的數字最大數字是a1,數值>=60的最小數字是a2

題目要求:輸出排序後的陣列,以及a1,a2,若不存在a1,則輸出"best case",若不存在a2,則輸出"worst case"

Solution: 認真操作一下就好了。排序的畫看要用bubble_sort, selection_sort, merge_sort, ....都可以,要用std::sort也可以,之後就掃過每個數字,稍微判斷一下就OK了。

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

const int MAX_N = 26;

int a[MAX_N];

int main () {
    int n;
    while (scanf("%d",&n) != EOF) {
        for (int i=1;n>=i;i++) {
            scanf("%d",&a[i]);
        }
        sort(a+1,a+n+1);
        int best=-1,worst=-1;
        for (int i=1;n>=i;i++) {
            if (i!=1) printf(" ");
            printf("%d",a[i]);
            if (a[i] >= 60 && worst==-1) worst=a[i];
            if (a[i] < 60) best=a[i];
        }
        puts("");
        if (best==-1) puts("best case");
        else printf("%d\n",best);
        if (worst==-1) puts("worst case");
        else printf("%d\n",worst);
    }
}

第二題:

一個R*C的矩陣(1<=R,C<=10),裡面的數值介於0~9,我們定義兩個操作:翻轉、旋轉。旋轉就是把R*C的矩陣90度順時針轉成C*R的矩陣,翻轉就是把第一列和最後一列對調,第二列和倒數第二列對調......等。

一開始,我們有矩陣A,經過m(1<=m<=10)個操過後,變成矩陣B。

題目要求:給你矩陣B和m個操作,請還原矩陣A

Solution : 我們要把B還原A,那就先把A-->B的順序調回B-->A,之後分兩個部分,兩個部份好好時做就OK了,旋轉注意要改回逆時針(如果不小心寫了順時針的,轉三次就變成逆時針了XD(我就是這個人)),翻轉就小心操作就好了。

實作小技巧:可以開另外一個暫存的陣列,先把原本的陣列操作過後的樣子存到暫存的陣列,之後再還原。

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

const int MAX_N = 12;

int a[MAX_N][MAX_N];
int tmp[MAX_N][MAX_N];
int oper[MAX_N];

int main () {
    int n,m,q;
    while (scanf("%d %d %d",&n,&m,&q) != EOF) {
        memset(a,-1,sizeof(a));
        for (int i=1;n>=i;i++) {
            for (int j=1;m>=j;j++) {
                scanf("%d",&a[i][j]);
            }
        }
        for (int i=1;q>=i;i++) {
            scanf("%d",&oper[i]);
        }
        for (int i=q;i>=1;i--){
            int k = oper[i];
            memset(tmp,-1,sizeof(tmp));
            if (k==1) {
                for (int i=1,ti=n;n>=i;i++,ti--) {
                    for (int j=1;m>=j;j++) {
                        tmp[ti][j] = a[i][j];
                    }
                }
                for (int i=1;n>=i;i++) {
                    for (int j=1;m>=j;j++) {
                        a[i][j] = tmp[i][j];
                    }
                }
            }
            else if (k==0) {
                swap(n,m);
                for (int i=1,ti=n;n>=i;i++,ti--) {
                    for (int j=1;m>=j;j++) {
                        tmp[i][j] = a[j][ti];
                    }
                }
                for (int i=1;n>=i;i++) {
                    for (int j=1;m>=j;j++) {
                        a[i][j] = tmp[i][j];
                    }
                }
            }
        }
        printf("%d %d\n",n,m);
        for (int i=1;n>=i;i++) {
            for (int j=1;m>=j;j++) {
                if (j!=1) printf(" ");
                printf("%d",a[i][j]);
            }
            puts("");
        }
    }
}

第三題:

這題就比較難了。以下的題解適用於最大的側資

題目要求:給你n個一維平面線段(1<n<10000),每個線段包含起始座標L,終點座標R (0<=L,R<=10,000,000),問說線段覆蓋的長度。

Solution:(1)是可能是你們的想法,但是不可行,(2)才OK
(1)就開一個陣列大小為10^7的bool陣列,每當一筆線段輸入進來的時候,就從L跑到R,在認真實作就好了。==>這樣複雜度是O(n) * O(10^7) //輸入跟跑陣列,時間,空間都有可能炸掉!要怎麼辦呢?

(2)我們把輸入的L,R先按照左界L排序(這時候一定要用merge_sort或std::sort),之後就開始拜訪我們的輸入,當新的L比舊的R還要大的時候,就代表"目前的線段已經結束",線段覆蓋面積+=R-L,反之,代表"線段延續",就跟新R=max(query的R, 原本的R)。==>時間複雜度=O(n lg n) + O(n) = O(n lg n),過了!!!

實作小技巧:可以把詢問寫一個struct或class,然後簡單定義一下這個struct的小於運算子,然後直接用std::sort,會很方面。或者是直接用std::pair,這個也很好用。

code : http://codepad.org/8LJMBn7p

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

const int MAX_N = 10004;
struct Node {
    int l;
    int r;
} node[MAX_N];

bool operator< (const Node& n1, const Node& n2) {  //sort時候所需要用的 
    return (n1.l<n2.l || n1.l==n2.l && n1.r<n2.r);
}

//其實上面的部分可以用std::pair取代
int main () { int n; while (scanf("%d",&n) != EOF) { for (int x=0;n>x;x++) { scanf("%d %d",&node[x].l,&node[x].r); } sort(node,node+n); //按照左界排序查詢 int nl=node[0].l; int nr=node[0].r; int ans=0; for (int x=1;n>x;x++) { int tl=node[x].l; int tr=node[x].r; if (tl>nr) { //新的左界比現在的右界大 --> 之前的左界~右界任務已經結束 ans += (nr-nl); nl=tl; //指定新的左界 nr=tr; //指定新的右界 } else if (tl<=nr && tr>nr) nr=tr; //新的右界比原本的右界好 } ans+=(nr-nl); //不要忘了 printf("%d\n",ans); } }


第四題:

這題就比較難了,以下解法適用於最大的側資

題目要求:給你一張血緣關係圖(n個人,n-1個關係,保證不會有環,最早的祖先只會有一個,1<=n<=10^5),請問血緣關係最遠兩位的距離!(其實就是樹直徑)

Solution :
我一開始想寫最短路,之後又想寫LCA,又想寫並查集(disjoint set),後來發現都不行

於是來寫個dfs吧!

先討論簡答可能出現的情況:a, 祖先圖是一條直線  b. 兩個兒子有共同的祖先



先把樹存好(需要記錄兒子和祖先,初始值:祖先=自己,兒子=沒有),然後抓到最頭的點,開始進行dfs,需要維護的是距離,根據兒子的case分成三種情況:

兒子數==0:直接return 0;
兒子數==1:return dfs(兒子) + 1
兒子數>=1 : 先把所有兒子的dfs值算出來,然後抓出最大的兩個於ans做比較,return max(dfs(兒子)) + 1

可是,這樣子寫,b情況考慮進去了,可是a好像沒有。

那就ans = max(ans, dfs(祖先) ); 就好啦!!!

code : http://codepad.org/sJtVSIbi

#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX_N = 100006;
struct Node {
    int ancestor;
    int num;         //小孩的個數 
    vector<int> child;  //因為不知道有多少個,所以使用vector 
} node[MAX_N];

int ans;

int dfs(int x) {
    if (node[x].num==0) return 0;
    else if (node[x].num==1) return dfs(node[x].child[0]) + 1;
    else {
        int ret[node[x].num];
        for (int i=0;node[x].num>i;i++) {
            ret[i]=dfs(node[x].child[i]) + 1;
        }
        sort(ret,ret+node[x].num);
        reverse(ret,ret+node[x].num);
        ans=max(ret[0]+ret[1],ans);
        return ret[0];
    }
}

int main () {
    int n;
    while (scanf("%d",&n) != EOF) {
        for (int x=0;n>x;x++) {       //記得先初始化 
            node[x].ancestor=x;
            node[x].num=0;
            node[x].child.clear();
        }
        for (int x=0;n-1>x;x++) {
            int a,b;
            scanf("%d %d",&a,&b);     //b的祖先是a
            node[a].num++;          //兒子的數量
            node[b].ancestor = a;    //指定祖先
            node[a].child.push_back(b); 
        }
        int id=0;
        ans=-1;
        for (int x=0;n>x;x++) {
            if (node[x].ancestor==x) id=x;     //尋找最老祖先 
        }
        ans=max(dfs(id),ans);  //一直線情形 
        printf("%d\n",ans);
    }
}



沒有留言:

張貼留言