【紀中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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章