Noip2008提高组总结

  Noip2008前三题是基础题,仔细一些都是可以AC的,第四题的证明很巧妙,但是看懂后代码其实很简单,感觉在这些大家都不屑去做的简单题中又学到了不少,四道题代码基本都是十几二十行就够了,渐渐感觉到,比代码和算法更重要的是思想与建模,觉得下阶段应该多注意培养自己的建模能力。

T1:火柴棒等式

  最简单的模拟题,首先记录下每种数字需要的火柴棒数,最后枚举验证即可。

+ ?





1

2

3

4

5

6

7

8

9

10

11

#include <cstdio>

int main(){

    int
n,ans=0,a[1001],b[10]={6,2,5,5,4,5,6,3,7,6};

    scanf("%d",&n);

    for(int
i=0;i<10;i++)a[i]=b[i];

    for(int
i=10;i<=1000;i++)a[i]=a[i%10]+a[i/10];

    for(int
i=0;i<=1000;i++)

    for(int
j=i;j<=1000;j++)if(a[i]+a[j-i]+a[j]+4==n)ans++;

    printf("%d\n",ans);

    return
0;

}

T2:笨小猴

  基础能力题,统计字符出现次数,然后直接用最简单的素数判断即可。

+ ?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

#include <cstdio>

#include <cmath>

#include <cstring>

using
namespace std;

int hash[26],max,min;

bool
pan(int
k){

    if(k==0||k==1)return
0;

    for(int
i=2;i<=(int)sqrt(k);i++)if(k%i==0)return
0;

    return
1;

}

int
main(){

    char
s[200]; min=100;

    scanf("%s",s);

    for(int
i=0;i<strlen(s);i++)hash[s[i]-‘a‘]++;

    for(int
i=0;i<26;i++){

        if(max<hash[i])max=hash[i];

        if(hash[i]&&hash[i]<min)min=hash[i];

    }int
ans=max-min;

    if(pan(ans)){puts("Lucky Word");printf("%d\n",ans);}

    else{puts("No Answer");puts("0");}

    return
0;

}

T3:传纸条

  对于这道题,其实不需要考虑从下往上传,直接考虑两条纸条从(1,1)开始传就可以了,可以采用动态规划,因为离开某个点之后,便不可能再回来。并且在转移时,判断同时转移的两点是否相同,若相同,不加数字,所以一定存在比其大的走法,不会影响结果。所以用四维Dp即可完成。

+ ?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#include <cstdio>

#include <algorithm>

using
namespace std;

int a[55][55],f[55][55][55][55],m,n;

int main(){

    scanf("%d%d",&n,&m);

    for(int
i=1;i<=n;i++)for(int
j=1;j<=m;j++)scanf("%d",&a[i][j]);

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

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

    for(int
h=1;h<=n;h++)

    for(int
k=1;k<=m;k++){

        f[i][j][h][k]=max(max(max(max(f[i][j][h][k],f[i-1][j][h-1][k]),f[i-1][j][h][k-1]),f[i][j-1][h][k-1]),f[i][j-1][h-1][k]);

        f[i][j][h][k]+=a[i][j];

        if(i!=h&&j!=k)f[i][j][h][k]+=a[h][k];

    }

    printf("%d\n",f[n][m][n][m]);

    return
0;

}

  然后可以考虑Dp的维数优化,因为纸条传的横坐标+纵坐标=走的步数,所以可以以步数为媒介将其优化为三维,代码如下:

+ ?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#include <cstdio>

#include <algorithm>

using
namespace std;

int a[55][55],f[110][55][55],m,n;

int main() 

    scanf("%d%d",&n,&m); 

    for(int
i=1;i<=n;i++)for(int
j=1;j<=m;j++)scanf("%d",&a[i][j]); 

    for(int
k=1;k<=n+m-2;k++) 

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

    for(int
j=1;j<=n;j++){ 

        f[k][i][j]=max(f[k-1][i-1][j],max(f[k-1][i][j-1],max(f[k-1][i][j],f[k-1][i-1][j-1]))); 

        f[k][i][j]+=a[i][k+2-i];

        if(i!=j&&k+2-i>=1&&k+2-j>=1)f[k][i][j]+=a[j][k+2-j];

    }

    printf("%d",f[n+m-2][n][n]); 

    return
0; 

 注意界限k+2-i>=1&&k+2-j>=1

T4:双栈排序

  首先给出结论P: S[i],S[j]两个元素不能进入同一个栈
等价于
存在k,满足i<j<k,使得S[k]<S[i]<S[j].

证明:

  考虑对于任意两个数q1[i]和q1[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个栈).实际上,这个条件p是:存在一个k,使得i<j<k且q1[k]<q1[i]<q1[j].
  首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.
  假设这两个数压入了同一个栈,那么在压入q1[k]的时候栈内情况如下:
  …q1[i]…q1[j]…
  因为q1[k]比q1[i]和q1[j]都小,所以很显然,当q1[k]没有被弹出的时候,另外两个数也都不能被弹出(否则q2中的数字顺序就不是1,2,3,…,n了).
而之后,无论其它的数字在什么时候被弹出,q1[j]总是会在q1[i]之前弹出.而q1[j]>q1[i],这显然是不正确的.
  接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈."
  不满足条件p有两种情况:一种是对于任意i<j<k且q1[i]<q1[j],q1[k]>q1[i];另一种是对于任意i<j,q1[i]>q1[j].
  第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起来,当q1[j]<q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j<k<r且q1[r]<q1[j]<q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没有影响).
  第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.
  这样,原命题的逆否命题得证,所以原命题得证.
  此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证.

  这样,我们对所有的数对(i,j)满足1<=i<j<=n,检查是否存在i<j<k满足p1[k]<p1[i]<p1[j].
  二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中.
  此时我们只考虑检查是否有解,所以只要O(n)检查出这个图是不是二分图,就可以得知是否有解.
  此时,检查有解的问题已经解决.接下来的问题是,如何找到字典序最小的解.
  实际上,可以发现,如果把二分图染成1和2两种颜色,那么结点染色为1对应当前结点被压入s1,为2对应被压入s2.为了字典序尽量小,我们希望让编号小的结点优先压入s1.
  又发现二分图的不同连通分量之间的染色是互不影响的,所以可以每次选取一个未染色的编号最小的结点,将它染色为1并从它开始DFS染色,直到所有结点都被染色为止.这样,我们就得到了每个结点应该压入哪个栈中.接下来要做的,只不过是模拟之后输出序列啦。
  还有就是注意复杂度的优化,如果对于数对(i,j),都去枚举检查是否存在k使得p1[k]<p1[i]<p1[j]的话,那么复杂度就升到了O(n^3).解决方法就是,首先预处理出数组min,min[i]表示从p1[i]到p1[n]中的最小值.接下来,只需要枚举所有数对(i,j),检查min[j+1]是否小于p1[i]且p1[i]是否小于p1[j]就可以了.

+ ?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

#include <cstdio>

int color[1005],min[1005],a[1005],s1[1005],s2[1005];

int n,m,i,j,k,t1,t2,now,sum;

const
int INF=~0U>>1; 

bool
dfs(int
x,int co){ 

    if(color[x]==0)color[x]=co; 

    else
return color[x]==co; 

    for(int
i=1;i<x;i++)if(a[i]<a[x]&&min[x+1]<a[i]&&!dfs(i,3-co))return
0; 

    for(int
i=x+1;i<=n;i++)if(a[x]<a[i]&&min[i+1]<a[x]&&!dfs(i,3-co))return
0;

    return
1; 

}

int
main(){ 

    scanf("%d",&n); 

    for(int
i=1;i<=n;i++)scanf("%d",&a[i]); min[n+1]=INF; 

    for(int
i=n;i>=1;i--){min[i]=min[i+1];if(a[i]<min[i])min[i]=a[i];} 

    for(int
i=1;i<=n;i++)if(color[i]==0&&!dfs(i,1)){puts("0");return
0;}

    now=1; t1=t2=0; 

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

        if(color[i]==1){printf("a ");s1[++t1]=a[i];} 

        else{printf("c ");s2[++t2]=a[i];} 

        while(s1[t1]==now||s2[t2]==now){ 

            if(s1[t1]==now++){t1--;printf("b ");} 

            else{t2--;printf("d ");} 

        

    }

    return
0;

}

注意点:

  1.Dp特别注意边界问题;

  2.Dp维数优化可以通过找中间量来实现,比如T3中的步数;

  3.注意是puts("0")而不是puts(0),否则会结果错误,但是编译可通过;

  4.不得不提自己又被双等号坑了一次,特别注意!!!  


Noip2008提高组总结,布布扣,bubuko.com

时间: 2024-10-12 19:55:50

Noip2008提高组总结的相关文章

NOIP2008提高组(前三题) -SilverN

此处为前三题,第四题将单独发布 火柴棒等式 题目描述 给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-9的拼法如图所示: 注意: 加号与等号各自需要两根火柴棍 如果A≠B,则A+B=C与B+A=C视为不同的等式(A.B.C>=0) n根火柴棍必须全部用上 输入输出格式 输入格式: 输入文件matches.in共一行,又一个整数n(n<=24). 输出格式: 输出文件matches.out共一行,

Noip2008提高组初赛 C

第十四届(NOIP2008)信息学奥赛联赛提高组C语言初赛试题 ● ●  全部试题答案均要求写在答卷纸上,写在试卷纸上一律无效  ●● 一. 单项选择题 (共10题,每题1.5分,共计15分.每题有且仅有一个正确答案). 1. 在以下各项中,(C  )不是操作系统软件. A. Solaris   B. Linux    C. Sybase     D. Windows Vista      E. Symbian A:索拉瑞斯是Sun Microsystems研发的计算机操作系统.它被认为是UNI

洛谷 P1125 笨小猴(NOIp2008提高组T1)

题目描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大! 这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小猴就认为这是个Lucky Word,这样的单词很可能就是正确的答案. 输入输出格式 输入格式: 输入文件word.in只有一行,是一个单词,其中只可能出现小写字母,并且长度小于100. 输出格

[NOIP2008] 提高组 洛谷P1125 笨小猴

题目描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大! 这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小猴就认为这是个Lucky Word,这样的单词很可能就是正确的答案. 输入输出格式 输入格式: 输入文件word.in只有一行,是一个单词,其中只可能出现小写字母,并且长度小于100. 输出格

noip2008提高组题解

第一题:笨小猴 模拟 ? 第二题:火柴棒等式 搜索 深搜不用说,确定出两个加数然后判断能否拼出等式. 枚举确实不太好搞,因为枚举范围不确定,太大了容易超时,太小了容易漏解.不过这题的数据貌似很温和,我从 0~1000 枚举也能过. ? 第三题:传纸条 多线程动态规划 跟 2000 年的方格取数很像. 还是看做两个人同时从左上角走到右下角.用 f(i, j, k) 表示横纵坐标和为 i,第一个人的行坐标为 j,第二个人的行坐标为 k 时的最大和. 由于一个地方不能走两次,所以对于除了终点以外的所有

【动态规划】Vijos P1493 传纸条(NOIP2008提高组第三题)

题目链接: https://vijos.org/p/1493 题目大意: 二取方格数,从(1,1)向下或向右走到(n,m)走两次,每个走到的格子值只能被取一次所能取到的最大值. (n,m<=50) 题目思路: [动态规划] f[i][j][k][l]表示第一次走到(i,j)第二次走到(k,l)的最大值. 分别向上或向左转移. 1 // 2 //by coolxxx 3 //#include<bits/stdc++.h> 4 #include<iostream> 5 #incl

2017.2.18[codevs1170]NOIP2008提高组复赛T4双栈排序

体面不贴 这题一开始卡了我好久--策了好久贪心都判断不了无解情况-- 直到看了题解才发现自己有多傻逼-- 传送门:http://blog.csdn.net/kqzxcmh/article/details/9566813 题解写的很清楚这里就不赘述了. 两次AC,还行吧. 关键是我太蒟蒻-- 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #in

Vijos P1496 火柴棒等式 【NOIP2008提高组第二题】

题目链接:https://vijos.org/p/1496 题目大意: 给你n(n<24)根火柴棍,你可以拼出多少个形如“A+B=C”的等式?("+"和"="各自需要两根火柴棍) 如果A≠B,则A+B=C与B+A=C视为不同的等式(A.B.C>=0) n根火柴棍必须全部用上 题目思路: 其实这题很水,n最大才24,扣掉+和=就只有20,直接枚举就行. 稍微算一下就知道每个数最大不会超过1111 两层for枚举每个数,判断是否用尽火柴即可. 1 // 2

洛谷-火柴棒等式-NOIP2008提高组复赛

题目描述 Description 给你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根火柴棍必须全部用上 输入输出格式 Input/output 输入格式: 输入文件matches.in共一行,又一个整数n(n<=24). 输出格式: 输出文件mat