前言
万年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的地方,不能走斜线,求最短数量。
输入
输入文件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表示列.
输出
输出文件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
解题思路
用分治算法,不断分,然后计算子问题。
代码
#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");
}//输出
}