noip 2015總結

神奇的幻方

題目描述

幻方是一種很神奇的NN矩陣:它由數字1,2,3,……,NN構成,且每行、每列及兩條對角線上的數字之和都相同。

當N為奇數時,我們可以通過以下方法構建一個幻方:
首先將1寫在第一行的中間。
之后,按如下方式從小到大依次填寫每個數K(K=2,3,…,N*N):

  1. 若(K?1)在第一行但不在最后一列,則將K填在最后一行,(K?1)所在列的右一列;
  2. 若(K?1)在最后一列但不在第一行,則將K填在第一列,(K?1)所在行的上一行;
  3. 若(K?1)在第一行最后一列,則將K填在(K?1)的正下方;
  4. 若(K?1)既不在第一行,也不在最后一列,如果(K?1)的右上方還未填數,則將K填在(K?1)的右上方,否則將K填在(K?1)的正下方。

現給定N請按上述方法構造N*N的幻方。

輸入輸出格式

輸入格式:
輸入文件只有一行,包含一個整數N即幻方的大小。

輸出格式:
輸出文件包含N行,每行N個整數,即按上述方法構造出的N*N的幻方。相鄰兩個整數之間用單個空格隔開。

輸入輸出樣例

輸入樣例#1:

3

輸出樣例#1:

8 1 6
3 5 7
4 9 2

思路

無難度模擬

代碼

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

int n,x,y;
int mp[200][200];
int main()
{
    scanf("%d",&n);
    x = 1,y = (n+1)/2;
    mp[x][y]=1;
    for(int i=2;i<=n*n;i++)
    {
        if(x==1&&y!=n)
            x=n,y++;
        else if(x!=1&&y==n)
            x--,y=1;
        else if(x==1&&y==n)
            x++;
        else if(!mp[x-1][y+1])
            x--,y++;
        else
            x++;
        mp[x][y]=i;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            printf("%d ",mp[i][j]);
        printf("\n");
    }
}

信息傳遞

題目描述

有n個同學(編號為1到n)正在玩一個信息傳遞的游戲。在游戲里每人都有一個固定的信息傳遞對象,其中,編號為i的同學的信息傳遞對象是編號為Ti同學。
游戲開始時,每人都只知道自己的生日。之后每一輪中,所有人會同時將自己當前所知的生日信息告訴各自的信息傳遞對象(注意:可能有人可以從若干人那里獲取信息,但是每人只會把信息告訴一個人,即自己的信息傳遞對象)。當有人從別人口中得知自己的生日時,游戲結束。請問該游戲一共可以進行幾輪?

輸入輸出格式

輸入格式:
輸入共2行。
第1行包含1個正整數n表示n個人。
第2行包含n個用空格隔開的正整數T1,T2,……,Tn其中第i個整數Ti示編號為i
的同學的信息傳遞對象是編號為Ti的同學,Ti≤n且Ti≠i
數據保證游戲一定會結束。

輸出格式:
輸出共 1 行,包含 1 個整數,表示游戲一共可以進行多少輪。

輸入輸出樣例
輸入樣例#1:52 4 2 3 1

輸出樣例#1:3

說明

樣例1解釋


游戲的流程如圖所示。當進行完第 3 輪游戲后, 4 號玩家會聽到 2 號玩家告訴他自
己的生日,所以答案為 3。當然,第 3 輪游戲后, 2 號玩家、 3 號玩家都能從自己的消息
來源得知自己的生日,同樣符合游戲結束的條件。

對于 30%的數據, n ≤ 200;
對于 60%的數據, n ≤ 2500;
對于 100%的數據, n ≤ 200000。

思路

找最小環
先把不可能的人踢了
即:先把入度為0的點刪除,然后把這個點指向的點的入度-1,如果入度為0,也刪去,這樣就只保留有用的點,那么從任意一個點開始,用vis數組記錄是否被訪問過,訪問到一個新節點就累加計數器,然后就做出來了.bfs和dfs都可以

代碼

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>

using namespace std;

int to[200010],vis[200010], rubian[200010];

int n,ans;

queue <int> q;

int main()
{
    memset(to, 0, sizeof(to));
    memset(vis, 0, sizeof(vis));
    memset(rubian, 0, sizeof(rubian));
    scanf("%d", &n);
    ans = n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &to[i]);
        ++rubian[to[i]];
    }
    for (int i = 1; i <= n; i++)
        if (rubian[i] == 0)
        {
            q.push(i);
            vis[i] = 1;
        }
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        --rubian[to[u]];
        if (rubian[to[u]] == 0)
        {
            vis[to[u]] = 1;
            q.push(to[u]);
        }
    }
    int temp,j;
    for (int i = 1; i <= n; i++)
    {
        if (vis[i] == 0 && rubian[i] != 0)
        {
            vis[i] = 1;
            temp = 1;
            j = to[i];
            while (!vis[j])
            {
                vis[j] = 1;
                j = to[j];
                temp++;
            }
            if (temp <= ans)
                ans = temp;
        }
    }
    printf("%d\n", ans);

    return 0;
}

跳石頭

題目描述

這項比賽將在一條筆直的河道中進行,河道中分布著一些巨大巖石。組委會已經選擇好了兩塊巖石作為比賽起點和終點。在起點和終點之間,有 N 塊巖石(不含起點和終 點的巖石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的巖石,直至到達 終點。

為了提高比賽難度,組委會計劃移走一些巖石,使得選手們在比賽過程中的最短跳 躍距離盡可能長。由于預算限制,組委會至多從起點和終點之間移走 M 塊巖石(不能 移走起點和終點的巖石)。

輸入輸出格式

輸入格式:
輸入文件名為 stone.in。

輸入文件第一行包含三個整數 L,N,M,分別表示起點到終點的距離,起點和終 點之間的巖石數,以及組委會至多移走的巖石數。

接下來 N 行,每行一個整數,第 i 行的整數 Di(0 < Di < L)表示第 i 塊巖石與 起點的距離。這些巖石按與起點距離從小到大的順序給出,且不會有兩個巖石出現在同 一個位置。

輸出格式:
輸出文件名為 stone.out。 輸出文件只包含一個整數,即最短跳躍距離的最大值。

輸入輸出樣例

輸入樣例#1:

25 5 2 
2
11
14
17 
21

輸出樣例#1:

4

思路

我們對于一個長度x,想看看它是否可以符合刪除石頭數小于等于m,可以這樣做:

從位置的小到大掃遍所有石頭,用一個變量存儲上一個跳到的點。第一個與這上一個點的距離大于等于x的石頭即是下一個跳到的點。這里用了一點貪心的思想:因為如果不跳到第一個符合條件的點上,那么整個隊列的稀疏度就會提高,最終需要刪除的石頭也會更多。因為我們要取最優狀態,所以要保證跳過的石頭數最少。當然,如果某個石頭到終點的距離小于x,那它不能被統計到——所以得刪去后面這些無法跳到的石頭。我自認為這應該也是一個坑點(雖然我第一遍就判斷了)。

這樣,便求出了這個x是否可行,如果可行,那就往右邊二分,但要記得范圍要包括x;若不行,則往左邊二分,右限制不包括x。然后,二分到左右邊界相等,輸出即可。

然后此題就做完了。大家應該很容易理解。

代碼

#include<cstdio>
#include<algorithm>
using namespace std;
int sto[100000];//開大一點,保險
int main()
{
    int s,n,m;
    scanf("%d%d%d",&s,&n,&m);
    int zuo=1,you=s,mid;//所有邊界為1、s
    for(int i=0;i<n;i++)scanf("%d",&sto[i]);
    sort(sto,sto+n);//從小到大排序
    int sg,cnt,ii;
    while(zuo!=you)
    {
        mid=(zuo+you+1)>>1;//位運算加速
        sg=cnt=0;//初始化
        for(ii=0;ii<n;ii++)
        {
            if(s-sto[ii]<mid)break;//如解析中所述,若再跳x已超過終點,則不可取此點,它后面的也顯然不可取
            if(sto[ii]-sg<mid)cnt++;//跳過
            else sg=sto[ii];//貪心,直接跳到
        }
        cnt+=n-ii;//統計最后被刪除的點數
        if(cnt<=m)zuo=mid;
        else you=mid-1;//二分邊界更新,具體請見解析
    }
    printf("%d",zuo);//輸出
    return 0;
}

子串

題目描述

有兩個僅包含小寫英文字母的字符串 A 和 B。現在要從字符串 A 中取出 k 個互不重疊的非空子串,然后把這 k 個子串按照其在字符串 A 中出現的順序依次連接起來得到一 個新的字符串,請問有多少種方案可以使得這個新串與字符串 B 相等?注意:子串取出 的位置不同也認為是不同的方案。

輸入輸出格式

輸入格式:
輸入文件名為 substring.in。
第一行是三個正整數 n,m,k,分別表示字符串 A 的長度,字符串 B 的長度,以及問
題描述中所提到的 k,每兩個整數之間用一個空格隔開。 第二行包含一個長度為 n 的字符串,表示字符串 A。 第三行包含一個長度為 m 的字符串,表示字符串 B。

輸出格式:
輸出文件名為 substring.out。 輸出共一行,包含一個整數,表示所求方案數。由于答案可能很大,所以這里要求[b]輸出答案對 1,000,000,007 取模的結果。[/b]

輸入輸出樣例

輸入樣例#1:

6 3 1 
aabaab 
aab

輸出樣例#1:

2

輸入樣例#2:

6 3 2 
aabaab 
aab

輸出樣例#2:

7

輸入樣例#3:

6 3 3 
aabaab 
aab

輸出樣例#3:

7

說明


對于第 1 組數據:1≤n≤500,1≤m≤50,k=1;
對于第 2 組至第 3 組數據:1≤n≤500,1≤m≤50,k=2;
對于第 4 組至第 5 組數據:1≤n≤500,1≤m≤50,k=m;
對于第 1 組至第 7 組數據:1≤n≤500,1≤m≤50,1≤k≤m;
對于第 1 組至第 9 組數據:1≤n≤1000,1≤m≤100,1≤k≤m;
對于所有 10 組數據:1≤n≤1000,1≤m≤200,1≤k≤m。

思路

最后數組壓到二維的就可以省空間了。

代碼

//不壓維
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1000 + 5, maxm = 200 + 5, moder = 1000000007;
int m, n, K;
char stra[maxn], strb[maxm];
int f[maxn][maxm][maxm], s[maxn][maxm][maxm];

void solve() {
    s[0][0][0] = 1;
    for(int i = 1; i <= n; ++i) {
        s[i][0][0] = 1; //注意如果不壓到二維,這是需要的
        for(int j = 1; j <= m; ++j)
            if(stra[i-1] == strb[j-1]) {
                for(int k = 1; k <= min(K, j); ++k) {
                    f[i][j][k] = (s[i-1][j-1][k-1] + f[i-1][j-1][k]) % moder,
                    s[i][j][k] = (s[i-1][j][k] + f[i][j][k]) % moder;
                }
            }else for (int k = 1; k <= min(K, j); ++k) s[i][j][k] = s[i-1][j][k]; //注意如果不壓到二維,這是需要的
    }
    printf("%d\n", s[n][m][K]);
}

int main() {
    scanf("%d%d%d%s%s", &n, &m, &K, stra, strb);
    solve();
    return 0;
}
//壓維
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1000 + 5, maxm = 200 + 5, moder = 1000000007;
int m, n, K;
char stra[maxn], strb[maxm];
int f[maxm][maxm], s[maxm][maxm];

void solve() {
    s[0][0] = 1;
    for(int i = 1; i <= n; ++i) {
        for(int j = m; j > 0; --j)
            if(stra[i-1] == strb[j-1]) {
                for(int k = min(K, j); k > 0; --k) {
                    f[j][k] = (s[j-1][k-1] + f[j-1][k]) % moder,
                    s[j][k] = (s[j][k] + f[j][k]) % moder;
                }
            }else fill(f[j], f[j] + min(K, j) + 1, 0);
    }
    printf("%d\n", s[m][K]);
}

int main() {
    scanf("%d%d%d%s%s", &n, &m, &K, stra, strb);
    solve();
    return 0;
}

斗地主

題目描述

牛牛最近迷上了一種叫斗地主的撲克游戲。斗地主是一種使用黑桃、紅心、梅花、方片的A到K加上大小王的共54張牌來進行的撲克牌游戲。在斗地主中,牌的大小關系根據牌的數碼表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不對牌的大小產生影響。每一局游戲中,一副手牌由n張牌組成。游戲者每次可以根據規定的牌型進行出牌,首先打光自己的手牌一方取得游戲的勝利。

現在,牛牛只想知道,對于自己的若干組手牌,分別最少需要多少次出牌可以將它們打光。請你幫他解決這個問題。

需要注意的是,本題中游戲者每次可以出手的牌型與一般的斗地主相似而略有不同。

具體規則如下:

輸入輸出格式

輸入格式:
第一行包含用空格隔開的2個正整數T和n,表示手牌的組數以及每組手牌的張數。

接下來T組數據,每組數據n行,每行一個非負整數對aibi表示一張牌,其中ai示牌的數碼,bi表示牌的花色,中間用空格隔開。特別的,我們用1來表示數碼A,11表示數碼J,12表示數碼Q,13表示數碼K;黑桃、紅心、梅花、方片分別用1-4來表示;小王的表示方法為01,大王的表示方法為02。

輸出格式:
共T行,每行一個整數,表示打光第i手牌的最少次數。

輸入輸出樣例

輸入樣例#1:

1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1

輸出樣例#1:

3

思路

搜索+剪枝

忽略花色,統計每種碼數出現次數方便出牌。
每次都先出順子,對于手中剩下的牌我們貪心地將剩下的組合牌需要打的次數計算出來,然后更新ans以剪枝。
雙王算作對牌。順子不包括2和雙王。

代碼

  #include<cstdio>
  #include<cstring>
  #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
  using namespace std;
  const int N = 25;
  int a[N],c[N];
  int n,T,ans;
 
 int Qans() {
     memset(c,0,sizeof(c));
     FOR(i,0,13) c[a[i]]++;
     int tot=0;                                    //tot帶牌 
     while(c[4]&&c[2]>1)  c[4]--,c[2]-=2,tot++;      
     while(c[4]&&c[1]>1) c[4]--,c[1]-=2,tot++;
     while(c[4]&&c[2]) c[4]--,c[2]--,tot++;
     while(c[3]&&c[2]) c[3]--,c[2]--,tot++;
     while(c[3]&&c[1]) c[3]--,c[1]--,tot++;
     return tot+c[1]+c[2]+c[3]+c[4];                //帶牌+三張 對子 單張 
 }
 
 void dfs(int now) {
     if(now>=ans) return ;
     int tmp=Qans();
     if(now+tmp<ans)  ans=now+tmp;
     FOR(i,2,13) {                                //三順子 
         int j=i;
         while(a[j]>=3) j++;
         if(j-i>=2) {
             FOR(j2,i+1,j-1) {
                 FOR(k,i,j2) a[k]-=3;
                 dfs(now+1);
                 FOR(k,i,j2) a[k]+=3;
             }
         }
     }
     FOR(i,2,13) {                                //雙順子 
         int j=i;
         while(a[j]>=2) j++;
         if(j-i>=3) {
             FOR(j2,i+2,j-1) {
                 FOR(k,i,j2) a[k]-=2;
                 dfs(now+1);
                 FOR(k,i,j2) a[k]+=2;
             }
         }
     }
     FOR(i,2,13) {                                //單順子 
        int j=i;
         while(a[j]>=1) j++;
         if(j-i>=5) {
             FOR(j2,i+4,j-1) {
                 FOR(k,i,j2) a[k]--;
                 dfs(now+1);
                 FOR(k,i,j2) a[k]++;
             }
         }
     }
 }
 
 int main() {
     //freopen("in.in","r",stdin);
     //freopen("out.out","w",stdout);
     scanf("%d%d",&T,&n);
     while(T--) {
         memset(a,0,sizeof(a));
         int x,y;
         FOR(i,1,n) {
             scanf("%d%d",&x,&y);
             if(x==1) x=13; else if(x) x--;
             a[x]++;
         }
         ans=1e9;
         dfs(0);
         printf("%d\n",ans);
     }
     return 0;
 }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容

  • 生活大爆炸版石頭剪刀布 題目描述 石頭剪刀布是常見的猜拳游戲:石頭勝剪刀,剪刀勝布,布勝石頭。如果兩個人出拳一樣,...
    bbqub閱讀 460評論 0 0
  • Vigenère 密碼 題目描述 16 世紀法國外交家 Blaise de Vigenère 設計了一種多表密碼加...
    bbqub閱讀 722評論 0 0
  • 樹形動態規劃,顧名思義就是樹+DP,先分別回顧一下基本內容吧:動態規劃:問題可以分解成若干相互聯系的階段,在每一個...
    Mr_chong閱讀 1,495評論 0 2
  • 風淺淺,月盈盈。云裳舞扇歌吹庭。金花悄入杯中酒,酎飲甜香慰倦程。
    木木無紋閱讀 382評論 0 4
  • 了解晨讀活動請戳→《王路晨讀晚課群晨讀活動須知》 即日起,開始加入前一天晚上同學們做的晚課。何謂晚課呢?晚課是王路...
    王路讀者群1閱讀 3,226評論 0 3