NOIP2008年复赛
一:
1. 笨小猴
(word.pas/c/cpp)
【问题描述】
笨小猴的词汇量很小,所以每次做英语选择题的时候都很头痛。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!
这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小猴就认为这是个Lucky Word,这样的单词很可能就是正确的答案。
【输入】
输入文件word.in只有一行,是一个单词,其中只可能出现小写字母,并且长度小于100。
【输出】
输出文件word.out共两行,第一行是一个字符串,假设输入的的单词是Lucky Word,那么输出“Lucky Word”,否则输出“No Answer”;
第二行是一个整数,如果输入单词是Lucky Word,输出maxn-minn的值,否则输出0。
【输入输出样例1】
word.in word.out
error Lucky Word
2
【输入输出样例1解释】
单词error中出现最多的字母r出现了3次,出现次数最少的字母出现了1次,3-1=2,2是质数。
【输入输出样例2】
word.in word.out
olympic No Answer
0
【输入输出样例2解释】
单词olympic中出现最多的字母i出现了2次,出现次数最少的字母出现了1次,2-1=1,1不是质数。
由于n<100所以直接用桶排,然后判断max-min是否为素数就可以了(可以只用根号n判断素数但数据太水随便了)
/********************
测试通过 Accepted
总耗时: 3 ms
0 / 0 数据通过测试.
运行结果
测试点#word1.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word10.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#word2.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word3.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word4.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word5.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#word6.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word7.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word8.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#word9.in 结果:AC 内存使用量: 256kB 时间使用量: 1ms
*********************/
#include
#include
#include
#include
using namespace std;
const int oo = 0x3f3f3f3f;
char s[105];
int a[30];
int main() {
scanf( "%s", &s );
int len = strlen (s);
memset( a, 0, sizeof(a) );
for ( int i = 0; i < len; i++ )
a[s[i]-'a']++;
int maxx = 0, minn = oo;
for ( int i = 0; i < 26; i++ ) {
if ( a[i] != 0 ) {
if ( a[i] > maxx ) maxx = a[i];
if ( a[i] < minn ) minn = a[i];
}
}
int num = maxx - minn;
int j;
for ( j = 2; j <= num - 1; j++ )
if ( num%j == 0 ) break;
if ( j == num ) {
printf("Lucky Word\n");
printf("%d\n",num);
return 0;
}
else {
printf("No Answer\n");
printf("0\n");
return 0;
}
}
二:
2. 火柴棒等式
(matches.pas/c/cpp)
【问题描述】
给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A、B、C是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。用火柴棍拼数字0-9的拼法如图所示:
注意:
1. 加号与等号各自需要两根火柴棍
2. 如果A≠B,则A+B=C与B+A=C视为不同的等式(A、B、C>=0)
3. n根火柴棍必须全部用上
【输入】
输入文件matches.in共一行,又一个整数n(n<=24)。
【输出】
输出文件matches.out共一行,表示能拼成的不同等式的数目。
【输入输出样例1】
matches.in matches.out
14 2
【输入输出样例1解释】
2个等式为0+1=1和1+0=1。
【输入输出样例2】
matches.in matches.out
18 9
【输入输出样例2解释】
9个等式为:
0+4=4
0+11=11
1+10=11
2+2=4
2+7=9
4+0=4
7+2=9
10+1=11
11+0=11
暴力,这道题打表可以过,因为n<24 再减去四根火柴就最大20
那么20火柴组成的最大的数字为11111,那么直接把1—11111这些数字的火柴根数算出来就可以了在枚举两个加数判断
/********************
测试通过 Accepted
总耗时: 10 ms
0 / 0 数据通过测试.
运行结果
测试点#matches1.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
测试点#matches10.in 结果:AC 内存使用量: 360kB 时间使用量: 1ms
测试点#matches2.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
测试点#matches3.in 结果:AC 内存使用量: 360kB 时间使用量: 1ms
测试点#matches4.in 结果:AC 内存使用量: 360kB 时间使用量: 1ms
测试点#matches5.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
测试点#matches6.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
测试点#matches7.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
测试点#matches8.in 结果:AC 内存使用量: 360kB 时间使用量: 1ms
测试点#matches9.in 结果:AC 内存使用量: 364kB 时间使用量: 1ms
********************/
#include
#include
#include
using namespace std;
struct date{
int tot, num[12000];
date() {
tot = 0;
}
};
date gun[30];
int a[11] = {6,2,5,5,4,5,6,3,7,6};
int n;
int main() {
freopen("matches.in","r",stdin);
freopen("matches.out","w",stdout);
int res = 0;
scanf( "%d", &n );
n = n - 4;
for ( int i = 0; i <= 12000; i++ ) {
int k = i, sum = 0;
do
{
sum += a[k%10];
k /= 10;
}while ( k!= 0 );
if ( sum <= n )
gun[sum].tot++;
gun[sum].num[gun[sum].tot] = i;
}
for ( int i = 2; i <= n; i++ ) {
for ( int j = 2; j <= n; j++ ) {
int last = n - i - j;
if ( last < 0 ) continue;
for ( int a1 = 1; a1 <= gun[i].tot; a1++ )
for ( int b1 = 1; b1 <= gun[j].tot; b1++ )
for ( int c1 = 1; c1 <= gun[last].tot; c1++ )
if ( (gun[i].num[a1] + gun[j].num[b1]) == gun[last].num[c1] ) {
// printf("%d %d %d\n",gun[i].num[a1], gun[j].num[b1], gun[last].num[c1]);
res++;
}
}
}
printf( "%d\n", res );
return 0;
}
三:
3. 传纸条
(message.pas/c/cpp)
【问题描述】
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行 、n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。
【输入】
输入文件message.in的第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。
接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。
【输出】
输出文件message.out共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
【输入输出样例】
message.in message.out
3 3
0 3 9
2 8 5
5 7 0 34
【限制】
30%的数据满足:1<=m,n<=10
100%的数据满足:1<=m,n<=50
此题用dp的做法,因为从右下往左上可以等价看为从右上往左下脚走,两种是相同的,那么用四维的数组保存状态
dp【i】【j】【k】【p】表示为走到两个点(i,j)(k,p)所可以拿到的最大的好感度那么
状态转移方程可以轻松的写出来详情见代码
/********************
测试通过 Accepted
总耗时: 113 ms
0 / 0 数据通过测试.
运行结果
测试点#message1.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#message10.in 结果:AC 内存使用量: 23532kB 时间使用量: 30ms
测试点#message2.in 结果:AC 内存使用量: 256kB 时间使用量: 1ms
测试点#message3.in 结果:AC 内存使用量: 492kB 时间使用量: 1ms
测试点#message4.in 结果:AC 内存使用量: 1004kB 时间使用量: 0ms
测试点#message5.in 结果:AC 内存使用量: 2540kB 时间使用量: 0ms
测试点#message6.in 结果:AC 内存使用量: 7276kB 时间使用量: 4ms
测试点#message7.in 结果:AC 内存使用量: 18408kB 时间使用量: 20ms
测试点#message8.in 结果:AC 内存使用量: 21100kB 时间使用量: 25ms
测试点#message9.in 结果:AC 内存使用量: 23532kB 时间使用量: 32ms
********************/
#include
#include
#include
#include
using namespace std;
int mp[51][51];
int dp[51][51][51][51];
int m, n;
int main() {
scanf( "%d%d", &m, &n );
for ( int i = 1; i <= m; i++ )
for ( int j = 1; j <= n; j++ ) scanf( "%d", &mp[i][j] );
for ( int i = 1; i <= m; i++ )
for ( int j = 1; j <= n; j++ )
for ( int k = 1; k <= m; k++ )
for ( int p = 1; p <= n; p++ ) {
if ( i == k && j == p && i != m && j != n && k != m && p != n )
continue;
if ( (i + j) != (k + p) ) continue;
int tmp1 = 0;
int i1, i2, j1, j2, k1, k2, p1, p2;
i1 = i; j1 = j - 1;
i2 = i - 1; j2 = j;
k1 = k; p1 = p - 1;
k2 = k - 1; p2 = p;
if ( i1 != k1 || j1 != p1 )
tmp1 = max( tmp1, dp[i1][j1][k1][p1] );
if ( i1 != k2 || j1 != p2 )
tmp1 = max( tmp1, dp[i1][j1][k2][p2] );
if ( i2 != k1 || j1 != p1 )
tmp1 = max( tmp1, dp[i2][j2][k1][p1] );
if ( i2 != k2 || j2 != p2 )
tmp1 = max( tmp1, dp[i2][j2][k2][p2] );
dp[i][j][k][p] = tmp1 + mp[i][j] + mp[k][p];
}
printf( "%d\n", dp[m][n][m][n] );
return 0;
}
用二分图匹配
对于Stack如果要从小到大输出有一个性质就是
i小于j ,p[i] 和p [j]代表进stack的元素大小
i、j表示进Stack的顺序,如果存在k>j且p[k]小于a[i]那么无论如何i、j都无法按规则输出。
所以我们找到这样的i、j然后把他们两个之间连一条边,表示其两个不能在一个stack中,那么到最后如果可以用两个stack输出就必然为一个二分图,因为如果一个集合里两点有边则不成立。
对于如何找这样两个i、j我们设一个后缀最小数组f[i]表示从i以后的连续数列的最小值。
在建成图后我们进行染色,将其中一个染色为1,一个染色为2,那么最后我们进行模拟就可以了按照a、b、c、d的优先进行模拟
(笔者一开始未想到如何正解,所以快速编了一个模拟可以过3个点)
/********************
测试通过 Accepted
总耗时: 5 ms
0 / 0 数据通过测试.
运行结果
测试点#twostack1.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#twostack10.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#twostack2.in 结果:AC 内存使用量: 256kB 时间使用量: 1ms
测试点#twostack3.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#twostack4.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#twostack5.in 结果:AC 内存使用量: 256kB 时间使用量: 1ms
测试点#twostack6.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#twostack7.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#twostack8.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#twostack9.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
********************/
#include
#include
#include
using namespace std;
struct Edge{
int v, next;
};
Edge e[2005];
int head[1005], num, n, aa[1005], f[1005], col[1005], sa[1005], sb[1005], ta, tb, pos = 1;
void adde( int i, int j ) {
e[++num].v = j;
e[num].next = head[i];
head[i] = num;
e[++num].v = i;
e[num].next = head[j];
head[j] = num;
}
void dfs( int u, int c ) {
col[u] = c;
for ( int i = head[u]; i; i = e[i].next ) {
int v = e[i].v;
if ( col[v] == c ) {
printf("0\n");
exit(0);
}
if ( !col[v] ) dfs(v, 3 - c);
}
}
int main() {
scanf( "%d", &n );
for ( int i = 1; i <= n; i++ ) scanf( "%d", &aa[i] );
f[n + 1] = 0x3f3f3f3f;
for ( int i = n; i >= 1; i-- ) f[i] = min( f[i + 1], aa[i] );
for ( int i = 1; i < n; i++ )
for ( int j = i + 1; j <= n; j++ ) if ( aa[i] < aa[j] && f[j + 1] < aa[i] ) adde(i, j);
for ( int i = 1; i <= n; i++ ) if ( !col[i] ) dfs(i, 1);
for ( int i = 1; i <= n; i++ ) {
if ( col[i] == 1 ) {
sa[++ta] = aa[i];
printf("a ");
} else {
sb[++tb] = aa[i];
printf("c ");
}
while ( ta && sa[ta] == pos || tb && sb[tb] == pos ) {
if ( ta && sa[ta] == pos ) {
ta--;
printf("b ");
}
if ( tb && sb[tb] == pos ) {
tb--;
printf("d ");
}
pos++;
}
}
return 0;
}