初學acmer--讀《演算法競賽入門經典》筆記(p36-41)

vaeloverforever發表於2017-08-04

1.下面的程式執行結果是什麼?(p36

#include<stdio.h>
int main() 
{
	double i;
	for(i=9.9;i!=10;i+=0.1)
	{
		printf("%.lf\n",i);
	}
	return 0;
}
根據自己主觀感覺,以為結果是9.9

執行發現是

10
10
10
10
10
10
10
11
11
11
11
11
11
11
11
11
11
12
12
12
。。。。

無限迴圈下去了

再仔細思索 發現兩個問題

①double型別的數值進行運算得不到“數學上精確”的結果(詳見點選開啟連結

②按%.lf輸出,結果是忽略小數點後面的部分的

而按%x.ylf輸出的話,結果是總共佔x位,保留y位小數

接著稍稍改了程式碼,將for語句中的初始化語句改成i=10,執行結果不再無限迴圈了,也沒輸出



2.開燈問題(p39)

題目:有n盞燈,編號為1~n。第一個人把所有燈開啟,第二個人按下所有編號為2的倍數的開關(這些燈將被關掉),第三個按下所有編號為3的倍數的開關(其中關掉的燈將被開啟,開著的燈將被關掉)。以此類推,一共有k個人,問最後有哪些燈開著?輸入n和k,輸出開著的燈的編號。k<=n<=1000
樣例輸入:
7 3
樣例輸出:
1 5 6 7


分析:此題有一個特點:若數量較少時,人可以不借助任何外物就可以解決,但一旦數量過大,單憑人力就有些吃力了,所以就可以藉助計算機超凡的計算力來“模擬”問題情境,從而解決問題。用a[1],a[2],a[3],... ,a[n]表示編號為1,2,3,...,n的燈是否開著,開閉狀態可以用0和1表示。開關動作可以用取反來模擬(!0=1,!1=0)

程式碼如下:

#include<stdio.h>
#include<string.h>               //下面的memset函式所需的標頭檔案
#define maxn 1010
int a[maxn];
int main()
{
	int n,k,first=1;
	memset(a,0,sizeof(a));     //將陣列a中的元素初始化為0
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++)
	  for(int j=1;j<=n;j++)
	    if(j%i==0)  a[j]=!a[j];
   for(int i=1;i<=n;i++)
   if(a[i]) {
   	if(first)
   	first=0;                //輸出時保持空格,即除了第一個數,其餘所有的數輸出之前都先輸出一個空格
   	else
   	printf(" ");
   	printf("%d",i);
   }
   printf("\n");
   return 0;
}

3.蛇形填數(P39)

題目:在n*n方陣裡填入1,2,。。。,n*n,要求填成蛇形。例如,n=4時方陣為:

10 11 12 1

 9 16 13 2

 8 15 14 3

 7  6    5  4(方陣中,多餘的空格不必嚴格輸出。n<=8)

分析:同上一題特點一樣,此題可以通過“模擬”來解。仔細分析,“筆”的移動軌跡為:下,下,下,左,左,左,上,上,上,右,右,下,下,左,上

容易發現都是四個過程一個迴圈(先下,再左,再上,最後右)。而且每次轉向無非兩種情況:一是再走就出界(如從4到5),二是再走就要走到以前走的格子(如從12到13)

第一種情況可以輕易判斷,而第二種情況可以通過先將二維陣列全部初始化為0,通過陣列中元素是否為零判斷是否已經填過


程式碼如下:

#include<stdio.h>
#include<string.h>
#define maxn 20
int a[maxn][maxn];
int main()
{
	int n,x,y,tot=0;
	scanf("%d",&n);
	memset(a,0,sizeof(a));
	x=0;
	y=n-1;
	a[x][y]=1;
	tot=1;
	while(tot<n*n)
	{
		while(x+1<n&&!a[x+1][y])  a[++x][y]=++tot;
		while(y-1>=0&&!a[x][y-1])  a[x][--y]=++tot;
		while(x-1>=0&&!a[x-1][y])   a[--x][y]=++tot;
		while(y+1<n&&!a[x][y+1])   a[x][++y]=++tot;
	}
	for(int i=0;i<n;i++)
	 {
 		 for(int j=0;j<n;j++)
		  printf("%3d",a[i][j]);
		  printf("\n");
	 }
	 return 0;
}
ps:四條while語句都遵循一個原則,就是先判斷,後賦值,而不是賦完值再判斷,因為若是出錯,很難修改回來

提示:很多情況下,最好是在做一件事之前檢查是不是可以做,而不要做完再後悔,因為“悔棋”往往比較麻煩





相關文章