【纪中2020.3.21日】模拟赛题解

目录:

T1:石子游戏
T2:汉诺塔
T3:城市统计
T4:香樟树

这次的题目也全是GDKOI的题……不搞USACO的题了

正题:

T1:石子游戏

题目描述

小勇和小实是对好朋友,他们经常一起游戏。 今天他们玩的游戏是这样的:有一个由正方形石头铺成的地板,它的高是2,长度是N。 例如以下是N=3的情况:
在这里插入图片描述

现在他们轮流在上面放上长宽分别是1和2的矩形石块,可以横放也可以竖放,但要刚好铺在地板上两个未被覆盖的正方形石头上,当某人不能放上去时他就输了。
例如,某次游戏可能是这样的,小实横放石块在左上面,如下:
在这里插入图片描述
然后小勇横放石块在右下面,如下:
在这里插入图片描述
这时小实不能再放石块了,所以他输了。小勇比较礼让,他让小实先放。当然,以上的方法可能不是最好的,现在假如他们都绝顶聪明,请你编程判断究竟谁会赢。

输入

第一行一个整数C(1<=C<=100),表示测试数据的个数。 接下来有C行,每行为一个测试数据,每个测试数据只有一个整数N(1<=N<=100)。

输出

输出C行,每行输出相应测试数据的结果。对于每个结果,如果是小勇赢的话就输出xiaoyong,否则就是小实赢啦,输出xiaoshi。

样例输入

1
1

样例输出

xiaoshi

分析:

一开始,我还以为这是个类似田忌赛马的故事那样去决策。
结果结论十分简单:判断奇偶
因为小石先放的话,小勇只需跟着它的操作放就行
判断石子长度如果是奇数,小石赢,则小勇赢。

CODE:

#include<cmath>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
int n,x; 
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d",&x);
		if(x%2==1) printf("xiaoshi\n");  //判断奇偶
		else printf("xiaoyong\n");
	}
return 0;
}

T2:汉诺塔

题目描述

古老的汉诺塔问题是这样的:用最少的步数将N个半径互不相等的圆盘从1号柱利用2号柱全部移动到3号柱,在移动的过程中小盘要始终在大盘的上面。 现在再加上一个条件:不允许直接把盘从1号柱移动到3号柱,也不允许直接把盘从3号柱移动到1号柱。 把盘按半径从小到大用1到N编号。每种状态用N个整数表示,第i个整数表示i号盘所在的柱的编号。则N=2时的移动方案为: (1,1)=>(2,1)=>(3,1)=>(3,2)=>(2,2)=>(1,2)=>(1,3)=>(2,3)=>(3,3) 初始状态为第0步,编程求在某步数时的状态。

输入

输入文件的第一行为整数T(1<=T<=50000),表示输入数据的组数。 接下来T行,每行有两个整数N,M(1<=n<=19,0<=M<=移动N个圆盘所需的步数)。

输出

输出文件有T行。 对于每组输入数据,输出N个整数表示移动N个盘在M步时的状态,每两个数之间用一个空格隔开,行首和行末不要有多余的空格。

样例输入

4
2 0
2 5
3 0
3 1

样例输出

1 1
1 2
1 1 1
2 1 1

分析&说明:

mmp这道题我比赛提交时把文件名打错了……丢了100分!!
讲道理,这道题与经典汉诺塔差别挺大的。
我的做法是:递推。推出每次转移的状态,再根据周期求解。

CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std; 
const int dx[5]={1,2,3,2};  //汉诺塔分别移动步数
int n,a[20],sum; 
int main()
{
	freopen("hanoi.in","r",stdin);
	freopen("hanoi.out","w",stdout);
	a[0]=1;
	for(int i=1;i<=19;i++) a[i]=3*a[i-1];//表示第i个圆盘在每a[i]次时转移两次
	scanf("%d",&n); 
	int k,c; 
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&k,&c); 
	    if(!c)  //为0	
	    {
	    	for(int i=1;i<=k;i++)
	    		printf("%d ",1);  //直接输出1即可 
	    		printf("\n"); 
		}else{
			for(int i=1;i<=k;i++)
			{
				sum=(c/a[i]*2)+(c%a[i])/a[i-1];   //表示这个转移在总状态里为哪一个状态
				printf("%d ",dx[sum%4]);  //上面的步数周期
			}
			printf("\n"); 
		}
	}
}

T3:城市统计

题目描述

中山市的地图是一个nn的矩阵,其中标号为1的表示商业区,标号为0的表示居民区。为了考察市内居民区与商业区的距离,并对此作出评估,市长希望你能够编写一个程序完成这一任务。 居民区i到商业区的距离指的是到距离它最近的商业区j的距离(|Xi-Xj|+|Yi-Yj|)(你可以理解为他们的行列分别作差),而你将统计的是对于城市中的每一个区域k,以它为中心的(2r+1)(2r+1)的矩阵区域内所有居民区到商业区的距离总和。结果同样以n*n的矩阵形式输出。

输入

第一行为t,表示以下有t组数据,每组数据之间以空行隔开,以下:
第一行为n,r(1<=r<n<=150)
第二行起为一个n*n的矩阵。

输出

t组n*n的矩阵。每组用空行隔开

样例输入

1
4 1
1 0 0 0
1 1 0 0
0 1 1 0
0 1 0 0

样例输出

1 4 9 8
2 5 10 9
2 4 7 7
2 3 4 4

分析:

这道题分几步做:
BFS求出两个端点之间的距离
前缀和的方式将这些距离累加
根据题目再判断能否建造。

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m,n,k,t,a[151][151],b[151][151],f[40010][4],dis[151][151];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
void bfs(int x,int y){  //广搜求距离
	f[1][1]=x;f[1][2]=y;f[1][3]=0;
	dis[x][y]=k;
	int head=0,tail=1;
	while(head<tail){
		head++;
		for(int i=0;i<4;i++){
			int xx=f[head][1]+dx[i];
			int yy=f[head][2]+dy[i];
			if(xx<1||xx>n||yy<1||yy>n||dis[xx][yy]==k) continue;
			tail++;
			f[tail][1]=xx;
			f[tail][2]=yy;
			f[tail][3]=f[head][3]+1;
			if(a[xx][yy]==1){
				b[x][y]=f[tail][3];
				return;
			}
			dis[xx][yy]=k;
		}
	}
}
int main(){
	freopen("city.in","r",stdin);
	freopen("city.out","w",stdout);
	cin>>t;
	while(t--){
		k=0;
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>a[i][j];
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				k++;
				if(a[i][j]==0){
					bfs(i,j);  //每一个居民点就广搜
				}
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				a[i][j]=b[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];  //前缀和累加距离
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				int x=max(i-m,1);
				int y=max(j-m,1);
				int xx=min(i+m,n);
				int yy=min(j+m,n);
				cout<<a[xx][yy]-a[x-1][yy]-a[xx][y-1]+a[x-1][y-1]<<" ";  //输出前缀和
			}
			cout<<endl;
		}
		cout<<endl;
		memset(dis,0,sizeof(dis));  //结束一轮,清空数组
		memset(b,0,sizeof(b));
	}
	return 0;
}

T4:香樟树

题目描述

被誉为江南四大名木之一的香樟树很有特色,它的树皮粗糙,质地却很均匀,从来没有白杨树的斑斑驳驳、没有柳树的肿瘤结节;树枝树干一分为二、二分为四一路长去,不会偷工减料也不会画蛇添足;树冠的形态是球形的,在天空中画出优美的曲线。 除了上述优点之外,香樟树还有一个秘密武器。那就是……………………它凭借朴实、厚重的优秀品格赢得了小狐狸的青睐!!! 话说有一天,小狐狸正在湖边散步,忽然一阵风吹来,她赶紧闭上眼睛。当她再次睁开眼睛时,发现美丽的湖畔多出了一排整齐的香樟树。小狐狸非常兴奋,她对每棵树都观察入微,并且数出了它们的叶子个数。她觉得如果相邻两棵树的叶子个数互素是不和谐的。因此小狐狸想从一排香樟树中选出若干棵,在满足相邻两棵树的叶子个数不互素的条件下,使得树尽量多。

输入

第一行一个正整数n,表示有n棵香樟树。 第二行n个正整数,第i个数表示第i棵香樟树叶子的个数。

输出

一个正整数,表示最多能选多少棵树。

样例输入

6
6 2 3 15 8 5

样例输出

4

分析:

这道题用最长不下降子序列有60分,正解为:DP
推出最为核心的动态能量转移方程

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

看了看评论区,发现这样一句话:“第二层循环时,只要从i-logi*2开始循环就AC了”。
然后真就AC了!!只不过要打一个log函数.。

CODE:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int a[100010],n,f[100010],ans;
int check(int x,int y)
{
    if(x%y==0)
	return y;
    else
	return check(y,x%y);  //辗转相除
}
int log(int x){
  //log函数
    int t=0;
    while(x>0)
    {
		x=x/2;
		t++;
    }
    return t;
}
int main(){
    freopen("camphor.in","r",stdin);
    freopen("camphor.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
		scanf("%d",&a[i]);
		f[i]=1;
    }
    for(int i=2;i<=n;i++)
    {
		for(int j=i-log(i)*2;j<=i-1;j++)  //dalao提示
			if(j>0)
	    		if(check(a[i],a[j])>1)  //辗转相除合法
			    	f[i]=max(f[i],f[j]+1);  //套方程
    }
    for(int i=1;i<=n;i++)
        if(f[i]>ans)   //找个最大值
	   		ans=f[i];
    printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/105425026