1. 程式人生 > >2008年NOIP普及組複賽解題報告

2008年NOIP普及組複賽解題報告

2008NOIP普及組解題報告

王祺磊

(本份解題報告以C++為參考程式)

一、ISBN號碼

一道讓人很長知識的題目,但是題目所蘊含的解題方法,卻十分直接。你甚至可以不用一個迴圈就可以搞定這道題。因為每一位都是固定的。到底第幾位乘幾都是固定的。在這道題中只要控制好字串的位數,就可以很好的解決整個題目。

參考答案:

#include <cstdlib>

#include <fstream>

 

using namespace std;

 

ifstream fin("isbn.in");

ofstream fout("isbn.out");

 

int main(int argc, char *argv[])

{

  char f[12]={"0123456789X"};

  char s[20];

  fin >> s;

  int l , i , j;

  int x;

  l = strlen( s );

  x = (s[0] - '0') * 1 +(s[2] - '0') * 2 + (s[3] - '0') * 3 +

      (s[4] - '0') * 4 +(s[6] - '0') * 5 + (s[7] - '0') * 6 +

      (s[8] - '0') * 7 +(s[9] - '0') * 8 + (s[10] - '0') * 9;

  x %= 11;

  if (s[12] == f[x])

    fout << "Right" << endl;

  else

  {

    s[12]=f[x];

    fout << s << endl;

 

}

  return EXIT_SUCCESS;

}

在題目中,參與運算的,只是ISBN程式碼的第0234678910位的數字,所以直接對這幾位分別做*1*2*3*4*5*6*7*8*9的操作。再將結果對11取餘。再最後判斷的時候,最簡單的方法就是建立一個對應表。將固定的數值轉化為符號。在最後效驗位錯誤的時候用正確的效驗碼替代錯誤的,然後將字元陣列輸出,這應該是最快的方法。本題,作為第一題,難度適中。

 

二、排座位

這是一個似乎極有現實意義的題目。但是,貪心思想顯露的非常明顯。此題無非就是統計,每一行每一列如果要使用分割,會減少多少對說話可能。找其中允許的前K行和前L列輸出。

這道題其實讓人很難把握的就是它對於輸出的要求,大家往往忽略掉最簡單的部分。輸出的位置也要按從小到大的標號顯示。

參考程式:

#include <cstdlib>

#include <fstream>

 

using namespace std;

 

ifstream fin("seat.in");

ofstream fout("seat.out");

 

int main(int argc, char *argv[])

{

  int y[1010] , x[1010] , w[1010];

  int m , n , k , l , d;

  int x1 , y1 , x2 , y2;

  int i , j;

  fin >> m >> n >> k >> l >> d;

  for (i = 1; i <= m; i++)

    y[i]=0;

  for (i = 1; i <= n; i++)

    x[i]=0;

  for (i = 0; i < d; i++)

  {

    fin >> y1 >> x1 >> y2 >> x2;

    if (x1 == x2)

    {

      if (y1 > y2)

        swap(y1 , y2);

      y[y1]++;

    }

    else

      if (y1 == y2)

      {

        if (x1 > x2)

          swap(x1 , x2);

        x[x1]++;

      }

  }

  //y

  for (i = 0; i <= m;i++)

    w[i] = i;

  for (i = 1; i < m;i++)

    for (j = i + 1;j <= m;j++)

      if (y[i] < y[j])

      {

        swap(y[i] , y[j]);

        swap(w[i] , w[j]);

      }

  for (i = 1; i < k;i++)

    for (j = i + 1;j <= k;j++)

      if (w[i] > w[j])

        swap(w[i] , w[j]);

  fout << w[1];

  for (i = 2;i <= k;i++)

    fout << " " << w[i];

  fout << endl;

  //x

  for (i = 0; i <= n;i++)

    w[i] = i;

  for (i = 1; i < n;i++)

    for (j = i + 1;j <= n;j++)

      if (x[i] < x[j])

      {

        swap(x[i] , x[j]);

        swap(w[i] , w[j]);

      }

  for (i = 1; i < l;i++)

    for (j = i + 1;j <= l;j++)

      if (w[i] > w[j])

        swap(w[i] , w[j]);

  fout << w[1];

  for (i = 2;i <= l;i++)

    fout << " " << w[i];

  fout << endl;

  return EXIT_SUCCESS;

}

兩個陣列x[]y[],用來表示對應的x列和y行插入可以分開的對數,按照排序,找出前若干個位置,消去。但是,前多少個的位置要有序輸出,所以才需要對w[]進行排序;為避免行末空格,所以,先顯示第一個位置,然後先空格再位置的顯示模式,也是做題的基本功。此題作為第二題,演算法難度偏低,選手素質考察較強。

 

三、傳球遊戲

這是一道經典動態規劃的變形題,看過樹塔的選手,應該多少都對這道題有些感覺。而看過HDU1176的《免費餡餅》的選手,就更該感覺此題似曾相識。此題,就是將原為平面的二維陣列所做的動態規劃過程,變成首尾相接的筒形推演過程。從上到下,當前每一個單元的內容取決於上一個時刻此單元左右單元的內容之和。

參考程式:

#include <cstdlib>

#include <fstream>

 

using namespace std;

ifstream fin("ball.in");

ofstream fout("ball.out");

int main(int argc, char *argv[])

{

  int s[35][35]={0};

  int i , j, k , m , n;

  s[0][0]=1;

  fin >> n >> m;

  for (i = 1; i <= m; i++)

  {

    s[i][0]=s[i-1][n-1]+s[i-1][1];

    for (j = 1; j < n-1; j++)

      s[i][j]=s[i - 1][j - 1] + s[i - 1][j + 1];

    s[i][n - 1]=s[i - 1][n - 2]+s[i - 1][0];

  }

  fout << s[m][0] << endl;

  return EXIT_SUCCESS;

}

除對於首單元和末單元單獨處理外,其他部分都是單純的上一個狀態的左右單元的內容之和,最後輸出的是0點狀態的值。

    本題演算法變化較少,難度偏低。

四、立體圖

這是一道,考場選手細心程度的題目。題目本身沒有什麼太多的演算法內容。但是,對於控制最小輸出面積的部分,確實本題的重點。

因為題目描述,每一個單元放的格子是從上往下放的,但是,陣列的座標系是從上往下伸展的。所以,本題第一需要注意的點就是,在畫一個格子的函式部分,必須倒著畫出方格,在生出最終影象的二維陣列輸出時,倒著輸出結果即可。

第二個重點是,計算輸出的最大寬度是可以通過公式計算出來的,而最大高度,隨無法直接通過公式算出來,卻可以根據每一個單元格的格子數比較出來。

參考程式:

#include <cstdlib>

#include <fstream>

 

using namespace std;

 

ifstream fin("drawing.in");

ofstream fout("drawing.out");

 

char s[600][600];

void hua(int y , int x)

{

  s[y][x]='+';s[y][x+1]='-';s[y][x+2]='-';

  s[y][x+3]='-';s[y][x+4]='+';

  s[y+1][x]='|';s[y+1][x+1]=' ';s[y+1][x+2]=' ';

  s[y+1][x+3]=' ';s[y+1][x+4]='|';s[y+1][x+5]='/';

  s[y+2][x]='|';s[y+2][x+1]=' ';s[y+2][x+2]=' ';

  s[y+2][x+3]=' ';s[y+2][x+4]='|';s[y+2][x+5]=' ';

  s[y+2][x+6]='+';

  s[y+3][x]='+';s[y+3][x+1]='-';s[y+3][x+2]='-';

  s[y+3][x+3]='-';s[y+3][x+4]='+';s[y+3][x+5]=' ';

  s[y+3][x+6]='|';

  s[y+4][x+1]='/';s[y+4][x+2]=' ';s[y+4][x+3]=' ';

  s[y+4][x+4]=' ';s[y+4][x+5]='/';s[y+4][x+6]='|';

  s[y+5][x+2]='+';s[y+5][x+3]='-';s[y+5][x+4]='-';

  s[y+5][x+5]='-';s[y+5][x+6]='+';

}

int main(int argc, char *argv[])

{

  int a[55][55];

  int x1 , y1 , t , x , y;

  int i , j , k;

  int m , n;

  fin >> m >> n;

  x1=4*n+2*m+1;

  y1=0;

  for (i = 0;i < m; i++)

    for (j = 0 ; j < n;j++)

    {

      fin>>a[i][j];

      t = 3 * a[i][j] + 2 * (m - i) +1;

      if ( t > y1)

        y1 = t;

    }

  for (i=0;i<y1;i++)

    for (j=0;j<x1;j++)

      s[i][j]='.';

  for (i = 0; i < m ; i++)

    for (j = 0;j < n; j++)

      for (k = 0 ; k < a[i][j] ; k++)

      {

        x = 2 * (m - i - 1) + 4 * j;

        y = 2 * (m - i - 1) + 3 * k;

        hua(y , x);

      }

  for (i=y1-1;i>=0;i--)

  {

    for (j=0;j<x1;j++)

      fout<<s[i][j];

    fout<<endl;

  }

  return EXIT_SUCCESS;

}

參考程式中,x1y1即為最大寬度和最大高度。x1的計算非常直接,就是根據行數和列數計算出的。每增加一行格子,就增加2列顯示寬度;每增加一列格子就增加4列顯示寬度,再加上第一列的基礎邊,即為4*n+2*m+1。而每一個單元格的高度,也是根據它所在的行和格子數目而計算出的。

畫的過程,按照,從後到前,從左到右的順序畫出。最後,按照分析,反向輸出即可。

    本題,細心考察性較高,演算法考察性偏低。在第四題的位置,應屬簡單類。

 

附:

    本四道題目已上傳到http://judge.ybdevelop.cn/JudgeOnline中,歡迎有興趣的選手,線上參與測評。