2008年NOIP普及组解题报告
王祺磊
(本份解题报告以C++为参考程序)
一、ISBN号码
一道让人很长知识的题目,但是题目所蕴含的解题方法,却十分直接。你甚至可以不用一个循环就可以搞定这道题。因为每一位都是固定的。到底第几位乘几都是固定的。在这道题中只要控制好字符串的位数,就可以很好的解决整个题目。
参考答案:
#include
#include
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代码的第0,2,3,4,6,7,8,9,10位的数字,所以直接对这几位分别做*1,*2,*3,*4,*5,*6,*7,*8,*9的操作。再将结果对11取余。再最后判断的时候,最简单的方法就是建立一个对应表。将固定的数值转化为符号。在最后效验位错误的时候用正确的效验码替代错误的,然后将字符数组输出,这应该是最快的方法。本题,作为第一题,难度适中。
二、排座位
这是一个似乎极有现实意义的题目。但是,贪心思想显露的非常明显。此题无非就是统计,每一行每一列如果要使用分割,会减少多少对说话可能。找其中允许的前K行和前L列输出。
这道题其实让人很难把握的就是它对于输出的要求,大家往往忽略掉最简单的部分。输出的位置也要按从小到大的标号显示。
参考程序:
#include
#include
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
#include
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
#include
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
for (j=0;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
fout<
fout<
}
return EXIT_SUCCESS;
}
参考程序中,x1,y1即为最大宽度和最大高度。x1的计算非常直接,就是根据行数和列数计算出的。每增加一行格子,就增加2列显示宽度;每增加一列格子就增加4列显示宽度,再加上第一列的基础边,即为4*n+2*m+1。而每一个单元格的高度,也是根据它所在的行和格子数目而计算出的。
画的过程,按照,从后到前,从左到右的顺序画出。最后,按照分析,反向输出即可。
本题,细心考察性较高,算法考察性偏低。在第四题的位置,应属简单类。
附:
本四道题目已上传到http://judge.ybdevelop.cn/JudgeOnline中,欢迎有兴趣的选手,在线参与测评。