【jzoj】2018.2.3NOIP普及组——D组模拟赛

前言

万年D组系列…

正题


题目1:数池塘(jzoj1898)

有一个地方有一些积水,连着的积水是一个池塘,求池塘数。

输入

第1行:由空格隔开的两个整数:N和M
第2..N+1行:每行M个字符代表约翰农场的一排方格的状态。每个字符或者是’W’或者是’.’,字符之间没有空格。

输出

第1行:约翰农场上的池塘数

样例输入

10 12
W……..WW.
.WWW…..WWW
….WW…WW.
………WW.
………W..
..W……W..
.W.W…..WW.
W.W.W…..W.
.W.W……W.
..W…….W.

样例输出

3

解题思路

深搜不解释。

代码

#include<cstdio>
#include<iostream> 
using namespace std;
int n,m,s;
char c;
bool a[101][101];
void dfs(int x,int y)
{
    if (x<1 || y<1 || x>n || y>m || a[x][y]) return;//退出
    a[x][y]=true;//封
    dfs(x+1,y);
    dfs(x-1,y);
    dfs(x,y+1);
    dfs(x,y-1);
    dfs(x+1,y+1);
    dfs(x-1,y-1);
    dfs(x+1,y-1);
    dfs(x-1,y+1);//搜
}
int main()
{
    //freopen("lkcount.in","r",stdin);
    //freopen("lkcount.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
      for (int j=1;j<=m;j++)
      {
        cin>>c;//用cin输入比较保险,昨天就是没用cin错了
        if (c=='.') a[i][j]=true;//封
      }
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
          if (!a[i][j])//搜
          {
            dfs(i,j);
            s++;//累计数量
          }
    }
    printf("%d",s);
}

题目2:接苹果(jzoj1899)

有两颗苹果树,每分钟某一颗树会掉一颗苹果。开始在第一颗树下,只能移动c次的情况下在t秒最多能接到多少颗苹果。

输入

第1行:由空格隔开的两个整数:T和W
第2..T+1行:1或2(每分钟掉落苹果的树的编号)

输出

第一行:在贝茜移动次数不超过W的前提下她能接到的最多苹果数

样例输入

7 2
2
1
1
2
2
1
1

样例输出

6

解题思路

用dp,有两种情况就是移动或不移动,这里用三维数组
f[i][j][k]表示第i分钟,移动了j次,在第k颗树下(其实可以不用)

代码

#include<cstdio>
#include<iostream>
using namespace std;
int f[1001][31][2],t,w,a[1001],maxs;
int check(int t,int x)//能否接到苹果
{
    if (a[t]==x) return 1;
    return 0;
}
int main()
{
    //freopen("bcatch.in","r",stdin);
    //freopen("bcatch.out","w",stdout);
    scanf("%d%d",&t,&w);
    for (int i=1;i<=t;i++) scanf("%d",&a[i]);
    for (int i=1;i<=t;i++)
    {
      f[i][0][0]=f[i-1][0][0]+check(i,1);//不移动
      maxs=max(maxs,f[i][0][0]);
      for (int j=1;j<=w;j++)
      {
        f[i][j][0]=max(f[i-1][j-1][1]+check(i,2),f[i-1][j][0]+check(i,1));
        f[i][j][1]=max(f[i-1][j-1][0]+check(i,1),f[i-1][j][1]+check(i,2));//动态转移
        maxs=max(maxs,max(f[i][j][0],f[i][j][1]));//求最大值。
      }
    }
    printf("%d",maxs);
}

题目3:找数(jzoj1416)

输入一串序列,输出第k大的数。

输入

输入文件find.in,输入两行,第一行两个数N、K,N表示序列的长度,K表示要找在这个序列中的第K大的数。
第二行,N(N≤3000000)个数,用空格隔开.

输出

输出文件find.out,输出序列中的第K大的数。

样例输入

6 3
5 2 4 3 1 6

样例输出

4

自解

用小根堆存K个最大的数,然后输出堆顶

正解

c++算法库直接sort快排

代码

#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,a[3000001],num,x;
void upA(int x)//维护堆的性质
{
    while (x>1 && a[x]<a[x/2])
    {
        swap(a[x],a[x/2]);
        x/=2;
    }
}
void downA(int x)//维护堆的性质
{
    int y=0;
    while (x*2<=num && a[x]>a[x*2] || x*2+1<=num && a[x]>a[x*2+1])
    {
        y=x*2;
        if (y+1<=num && a[y]>a[y+1]) y++;
        swap(a[x],a[y]);
        x=y;
    }
}
int main()
{
    //freopen("find.in","r",stdin);
    //freopen("find.out","w",stdout);
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) 
    {
      scanf("%d",&x);//输入
      if (num<k) {a[++num]=x;upA(num);}//前k个
      else if (x>a[1])//选大的
      {
        a[1]=x;//替换
        downA(1);//维护
      }
    }
    printf("%d",a[1]);//输出
}

题目4:最短路径(jzoj1417)

没错这道题就像题目一样,没错,就是递推(才不是什么最短路)。一个n*m的地方,不能走斜线,求最短数量。
From jzoj

输入

输入文件Sline.in,一行,两个数M,N,其中 2

输出

输出文件sline.out,输出最短路的走法总数.

样例输入

7 5

样例输出

210

解题思路

首先,我们可以从左边或下面来,所以f[i][j]=f[i-1]+f[j-1]。然后因为数据忒大,有不需要取膜,又容易超时。
所以这道题的方法是:禁忌·压位高精滚动反复递推

代码

#include<cstdio>
using namespace std;
int n,m,f[2][801];
int a[1601][61];
void add(int n1,int n2,int n3)
{
    int g=0;
    for (int i=1;i<=60;i++)
    {
        a[n1][i]=a[n1][i]+a[n2][i]+g;
        g=a[n1][i]/100000000;
        a[n1][i]%=100000000;
        a[n3][i]=a[n1][i];
    }
}
void write(int n1)
{
    int w=60;
    while (a[n1][w]==0) w--;//找
    printf("%d",a[n1][w]);//输出
    for (int i=w-1;i>=1;i--)
    {
        if (a[n1][i]<10000000) printf("0");//暴力判断前导0
        if (a[n1][i]<1000000) printf("0");
        if (a[n1][i]<100000) printf("0");
        if (a[n1][i]<10000) printf("0");
        if (a[n1][i]<1000) printf("0");
        if (a[n1][i]<100) printf("0");
        if (a[n1][i]<10) printf("0");
        printf("%d",a[n1][i]);
    }
}
int main()
{
    //freopen("sline.in","r",stdin);
    //freopen("sline.out","w",stdout);
    scanf("%d%d",&n,&m);if (m>n) {int t=m;m=n;n=t;}//避免错误
    for (int i=1;i<=m;i++) {f[0][i]=i;f[1][i]=n+i;}//对应高精度数组号
    a[f[1][1]][1]=1;//初值
    for (int i=1;i<=n;i++)
    {
      for (int j=1;j<=m;j++)
      {
        if (i!=1 || j!=1)
        add(f[(i-1)%2][j],f[i%2][j-1],f[i%2][j]);//加
      }
    }
    write(f[n%2][m]);
}

题目5:棋盘覆盖(jzoj1418)

一个N*N的棋盘,用“L”字方块(覆盖3格)覆盖除了特殊方块棋盘(不能重叠)。求覆盖方法。

输入

输入文件Chessboard.in,共两行,第一行一个数N为棋盘的大小,N满足条件N=2^K,1<=K<=10.
第二行,两个数x,y,表示棋盘中那一个与其它方格不同的位置,x表示行,y表示列.
From jzoj

输出

输出文件Chessboard.out,输出N行N列,共N×N个数,表示用L 型(占3 个小格)纸片覆盖棋盘上除特殊方格的所有部分,各纸片不得重叠的方法.其中的数表示L型纸片覆盖的顺序编号, 不同的L型纸片用不同的编号,同一个L型纸片占据的三个位置相同,编号从1开始,特殊方格用-1标志;每两个数之间用一个空格隔开,每行最末一个数后面没有空格.

样例输入

4
2 2

样例输出

2 2 3 3
2 -1 1 3
4 1 1 5
4 4 5 5

解题思路

用分治算法,不断分,然后计算子问题。
From me
From me

代码

#include<cstdio>
using namespace std;
int number,dx,dy,n,a[1025][1025];
void dfs(int x,int y,int ux,int uy,int s)//x,y表示位置,ux,uy表示特殊符号位置,s表示边长
{
    if (s==1) return;//退出
    number++;//int zx=x+s/2;int zy=y+s/2;//中间位置
    if (ux<zx && uy<zy)//特殊格子在左上
    {
        a[zx-1][zy]=number;
        a[zx][zy-1]=number;
        a[zx][zy]=number;//记录
        dfs(x,y,ux,uy,s/2);//分治
        dfs(zx,y,zx,zy-1,s/2);
        dfs(x,zy,zx-1,zy,s/2);
        dfs(zx,zy,zx,zy,s/2);
    }
    else if (ux<zx && uy>=zy)
    {
        a[zx][zy-1]=number;
        a[zx-1][zy-1]=number;
        a[zx][zy]=number;
        dfs(x,y,zx-1,zy-1,s/2);
        dfs(x,zy,ux,uy,s/2);
        dfs(zx,y,zx,zy-1,s/2);
        dfs(zx,zy,zx,zy,s/2);
    }
    else if (ux>=zx && uy<zy)
    {
        a[zx-1][zy-1]=number;
        a[zx-1][zy]=number;
        a[zx][zy]=number;
        dfs(x,y,zx-1,zy-1,s/2);
        dfs(zx,y,ux,uy,s/2);
        dfs(x,zy,zx-1,zy,s/2);
        dfs(zx,zy,zx,zy,s/2);
    }
    else if (ux>=zx && uy>=zy)
    {
        a[zx-1][zy]=number;
        a[zx][zy-1]=number;
        a[zx-1][zy-1]=number;
        dfs(x,y,zx-1,zy-1,s/2);
        dfs(x,zy,zx-1,zy,s/2);
        dfs(zx,y,zx,zy-1,s/2);
        dfs(zx,zy,ux,uy,s/2);
    }
}
int main()
{
    //freopen("chessboard.in","r",stdin);
    //freopen("chessboard.out","w",stdout);
    scanf("%d%d%d",&n,&dx,&dy);
    a[dx][dy]=-1;//特殊方块
    dfs(1,1,dx,dy,n);//搜索
    for (int i=1;i<=n;i++)
    {
      for (int j=1;j<=n;j++)
        printf("%d ",a[i][j]);
      printf("\n");
    }//输出
} 

转载于:https://www.cnblogs.com/sslwyc/p/9218595.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值