1. 程式人生 > >NOIP2017——真題總結

NOIP2017——真題總結

NOIP來了又去了
於是他留下了四道題……(你以為我要抒情?)

1.成績(score)
題目描述
牛牛最近學習了C++入門課程,這門課程的總成績計算方法是:
總成績 = 作業成績× 20% + 小測成績× 30% + 期末考試成績× 50%
牛牛想知道,這門課程自己最終能得到多少分。
輸入
只有1 行,包含三個非負整數A、B、C,分別表示牛牛的作業成績、小測
成績和期末考試成績。相鄰兩個數之間用一個空格隔開,三項成績滿分都是100 分。
樣例輸入

100 100 80

樣例輸出

90

只有1 行,包含一個整數,即牛牛這門課程的總成績,滿分也是100 分。

思路:
/**************************************************


很簡單
A/10 * 2+B/10 * 3+C/10 * 5;
僅此而已
/**************************************************

2.圖書管理員(librarian)
題目描述
圖書館中每本書都有一個圖書編碼,可以用於快速檢索圖書,這個圖書編碼是一個
正整數。
每位借書的讀者手中有一個需求碼,這個需求碼也是一個正整數。如果一本書的圖
書編碼恰好以讀者的需求碼結尾,那麼這本書就是這位讀者所需要的。
小 D 剛剛當上圖書館的管理員,她知道圖書館裡所有書的圖書編碼,她請你幫她寫
一個程式,對於每一位讀者,求出他所需要的書中圖書編碼最小的那本書,如果沒有他
需要的書,請輸出-1。
輸入
第一行,包含兩個正整數n 和q,以一個空格分開,分別代表圖書館裡
書的數量和讀者的數量。
接下來的 n 行,每行包含一個正整數,代表圖書館裡某本書的圖書編碼。
接下來的 q 行,每行包含兩個正整數,以一個空格分開,第一個正整數代表圖書館
裡讀者的需求碼的長度,第二個正整數代表讀者的需求碼。
輸出
有q 行,每行包含一個整數,如果存在第i 個讀者所需要的書,則在第i
行輸出第i 個讀者所需要的書中圖書編碼最小的那本書的圖書編碼,否則輸出-1。
樣例輸入

5 5
2123
1123
23
24
24
2 23
3 123
3 124
2 12
2 12

樣例輸出

23
1123
-1
-1
-1

思路:
/******************************************************

其實這次的資料中圖書編碼是沒有前導0的……
如果有前導0,利用字元(串)陣列存,排序,輸入時查詢即可

/******************************************************

3.棋盤(chess)
題目描述

有一個m × m的棋盤,棋盤上每一個格子可能是紅色、黃色或沒有任何顏色的。你現在
要從棋盤的最左上角走到棋盤的最右下角。
任何一個時刻,你所站在的位置必須是有顏色的(不能是無色的),你只能向上、下、
左、右四個方向前進。當你從一個格子走向另一個格子時,如果兩個格子的顏色相同,那你
不需要花費金幣;如果不同,則你需要花費1 個金幣。
另外,你可以花費2 個金幣施展魔法讓下一個無色格子暫時變為你指定的顏色。但這個
魔法不能連續使用,而且這個魔法的持續時間很短,也就是說,如果你使用了這個魔法,走
到了這個暫時有顏色的格子上,你就不能繼續使用魔法;只有當你離開這個位置,走到一個
本來就有顏色的格子上的時候,你才能繼續使用這個魔法,而當你離開了這個位置(施展魔
法使得變為有顏色的格子)時,這個格子恢復為無色。
現在你要從棋盤的最左上角,走到棋盤的最右下角,求花費的最少金幣是多少?
輸入

第一行包含兩個正整數m,n,以一個空格分開,分別代表棋盤的大小,棋盤上
有顏色的格子的數量。
接下來的 n 行,每行三個正整數x,y,c,分別表示座標為(x,y)的格子有顏色c。
其中c=1 代表黃色,c=0 代表紅色。相鄰兩個數之間用一個空格隔開。棋盤左上角的座標
為(1, 1),右下角的座標為(m, m)。
棋盤上其餘的格子都是無色。保證棋盤的左上角,也就是(1,1)一定是有顏色的。

輸出
輸出一行,一個整數,表示花費的金幣的最小值,如果無法到達,輸出-1。

樣例輸入
5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0
樣例輸出
8

/******************************************************

這道題的難度較大,但是由於資料m<=100。
於是乎我想到了暴力深搜,結果……
其實就差記憶化(有點像DP)了(鬼知道為什麼關鍵時刻就記不起來了!)
用深搜的基本思想+用陣列存到達某個格子的最小代價=>“(詭異)機智的深搜”
考後滲透出來的程式碼。。。

#include<cstdio>
#define MAXM 100
#define MAXN 1000
int col[MAXN+5][MAXN+5],f[MAXM+5][MAXM+5];//用col[i][j]存格子(i,j)顏色
int m,n,tot;        //用f[i][j]存到達格子(i,j)當前的最小代價
void to(int,int,int,int,int);   //注意tot用來幫助深搜統計當前路徑所用的代價
void dfs(int x,int y,int now)//用now表示還可不可以用魔法
{
    if(f[x][y]<=tot)return;//用當前路徑走到(x,y)比之前的一條路貴,就可以不走了
    else f[x][y]=tot;      //否則到達這裡的最小代價就是當前路徑代價tot   
    to(x,y,x,y+1,now);
    to(x,y,x+1,y,now);
    to(x,y,x,y-1,now);
    to(x,y,x-1,y,now);             //在當前的點上有四個方向可以嘗試
}
void to(int fx,int fy,int tx,int ty,int z)
{
    if(z==1&&col[tx][ty]==0)return;//不能轉變前方格子且前方格子是白=>不能走
    else
    {
        if(col[tx][ty]==0){               //使用魔法走到下一個白格子的話
            tot+=2;                  //心痛錢錢三秒……
            col[tx][ty]=col[fx][fy];//變成和當前格子一樣的顏色(省錢)
            dfs(tx,ty,1);//繼續走,但是不能用魔法了
            col[tx][ty]=0;//一條路走完了,顏色變回來
            tot-=2;//錢收回來
    }
        else{
            if(col[fx][fy]==col[tx][ty])//如果前面格子有顏色且一樣
                dfs(tx,ty,0);//淡定走過
            else{tot+=1;dfs(tx,ty,0);tot-=1;}//有顏色還不一樣:交過路費
        }
    }
}
int main()
{
    scanf("%d %d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        scanf("%d",&col[x][y]);
    col[x][y]++;              //輸入格子顏色
    }
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
            f[i][j]=20000;       //將f陣列初始化,使它在以後的過程中再更新
    dfs(1,1,0);                   //愉快地記憶化動歸深搜
    if(f[m][m]!=20000)
        printf("%d",f[m][m]);//只要最後那裡(右下角)被更新過(走過)——輸出
    else
        printf("-1");//沒走過——輸出-1
    return 0;
}

/******************************************************

4.跳房子(jump)
題目描述

跳房子,也叫跳飛機,是一種世界性的兒童遊戲,也是中國民間傳統的體育遊戲之一。跳房子的遊戲規則如下:
在地面上確定一個起點,然後在起點右側畫n 個格子,這些格子都在同一條直線上。每個格子內有一個數字(整數),表示到達這個格子能得到的分數。玩家第一次從起點開始向右跳,跳到起點右側的一個格子內。第二次再從當前位置繼續向右跳,依此類推。規則規定:
玩家每次都必須跳到當前位置右側的一個格子內。玩家可以在任意時刻結束遊戲,獲得的分數為曾經到達過的格子中的數字之和。
現在小R 研發了一款彈跳機器人來參加這個遊戲。但是這個機器人有一個非常嚴重的缺陷,它每次向右彈跳的距離只能為固定的d。小R 希望改進他的機器人,如果他花g 個金幣改進他的機器人,那麼他的機器人靈活性就能增加g,但是需要注意的是,每次彈跳的距離至少為1。具體而言,當g < d時,他的機器人每次可以選擇向右彈跳的距離為d-g, d-g+1,d-g+2,…,d+g-2,d+g-1,d+g;否則(當g ≥ d時),他的機器人每次可以選擇向右彈跳的距離為1,2,3,…,d+g-2,d+g-1,d+g。
現在小R 希望獲得至少k 分,請問他至少要花多少金幣來改造他的機器人。
輸入

第一行三個正整數n,d,k,分別表示格子的數目,改進前機器人彈跳的固定距離,以及希望至少獲得的分數。相鄰兩個數之間用一個空格隔開。
接下來n 行,每行兩個正整數
輸出

共一行,一個整數,表示至少要花多少金幣來改造他的機器人。若無論如何他都無法獲
得至少k 分,輸出-1。

樣例輸入

7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

樣例輸出

2

思路:
/******************************************************

當時覺得這道題確實很jump啊
由於給出的可能的金幣數量範圍較小,於是乎就有了一種神奇的方法:二分答案
二分使用的金幣的數量:
先在可能的範圍內取得中間值,再判斷可不可以用這麼多金幣獲得足夠多的分數
可以的話=>right=mid;將金幣數量減少。不可以就left=mid;獎金幣數量增多。
這樣子的話後續操作中金幣數量就成為了一個定值。
那麼麻煩的就是判斷了
發現:對於一個座標為x(任意)的點,有 x-g,x-g+1,x-g+2,……,x,x+1,……,x+g這些點
可以跳到x,所以跳到x點的最大得分就是上述點中“最大的一個可以跳過來+當前點”
就像是DP,每一個點都可以由前面的點得到最優方案。
但是要挨個確定每一個點的最優值仍然非常麻煩,於是乎使用了單調佇列來優化。
單調佇列:使用一個下降(隊頭最大)佇列來存可以到達x的所有點的值,每次找下一個點時就將佇列的右邊壓入一個元素,同時維護佇列,判斷隊頭有沒有出界,這樣每次更新後隊頭就是可以到達x的區間的最大值了。
非常神奇……
至於程式碼的話,再等我下次滲透滲透。。。

/******************************************************