C語言初學者代碼中的常見錯誤與瑕疵(7)


問題:

矩形的個數

  在一個3*2的矩形中,可以找到6個1*1的矩形,4個2*1的矩形3個1*2的矩形,2個2*2的矩形,2個3*1的矩形和1個3*2的矩形,總共18個矩形。給出A,B,計算可以從中找到多少個矩形


輸入:
  本題有多組輸入數據(<10000),你必須處理到EOF為止

  輸入2個整數A,B(1<=A,B<=1000)


輸出:

  輸出找到的矩形數。


樣例:

輸入:

1 2
3 2


輸出:
3
18

 原代碼-1:

#include <stdio.h>
int rectangle(int x,int y)
{
    int i,j,sum,temp;

    for(i=1,sum=0,temp=0;i<=x;i++)
    {
        for(j=1;j<=y;j++)
        {
            temp = (x-i+1)*(y-j+1);
            sum += temp;
        }
    }

    return sum;
}

int main()
{
    long int A[1000],B[1000],i;
    int rectangle(int x,int y);
    //printf("input A&B and end with 0\n");

    for(i=0;i<1000;i++)
    {
        scanf("%d",&A[i]);
        
        if (A[i]==0)
        {
            break;
        }

        scanf("%d",&B[i]);
    }

    for(i=0;A[i]!=0;i++)
    {
        printf("%d\n",rectangle(A[i],B[i]));
    }

    return 0;
}

 評析:

   寫完之后這位小朋友坦率地承認:

露珠不知道如何以EOF結尾..只好用0了...哪位指點下..

   我告訴他,

沒必要用數組
while( scanf("%d%d", &A,&B)!=EOF )
{
     //計算輸出矩形個數
}

  於是他很快給出了新代碼:

 原代碼-2:

#include <stdio.h>
int rectangle(int x,int y)
{
    int i,j,sum,temp;

    for(i=1,sum=0,temp=0;i<=x;i++)
    {
        for(j=1;j<=y;j++)
        {
            temp = (x-i+1)*(y-j+1);
            sum += temp;
        }
    }

    return sum;
}

int main()
{
    long int A,B;
    int rectangle(int x,int y);
    printf("input A&B and end with EOF\n");

    while(scanf("%d%d",&A,&B)!= EOF)
    {
        printf("%d\n",rectangle(A,B));
    }

    return 0;
}

評析:

  這次好多了。不過他又有了新的困惑:

我修改成這樣之后,比如我先輸入 2 3 跳出來 18,輸入eof 就一直跳 18 停止不了了...
總之就是一直跳最后一次出來的結果 ..

  看來他不清楚EOF是怎么回事,還以為是在鍵盤上鍵入eof三個字符呢。

  我告訴他:

EOF不是三個字符
而是一個符號常量
如果你用的是WIN系統
在行首輸入Control-Z試試

  實際上EOF是在stdio.h中定義的一個宏,通常是這樣的 

#define    EOF    (-1)

  不過C語言並沒說EOF一定為-1。

  在鍵盤上是沒有這個EOF的,但在輸入流中遇到特殊的字符,scanf()函數的返回值可以是EOF。那么,這句話究竟是什么意思呢?

  通常scanf()的返回值是一個非負整數。比如

int i;
scanf("%d", &i ) ;

  如果你在鍵盤上鍵入的是123(1、2、3與%d相匹配),scanf("%d", &i )的值就為1,因為為1個變量賦了值;如果你在鍵盤上鍵的是abc(a與%d不匹配),scanf("%d", &i )的值就為0,因為scanf無法把"a"視為十進制整數(%d),也無法對它進行轉換,更無法為變量 i 賦值,也就是說scanf這種情況下沒有為任何變量賦值,所以返回值為0。如果scanf()在輸入流中遇到的是某個特殊的字符(具體是哪個字符與環境有關),則返回值為EOF。

  另外我告訴他:

把函數類型聲明
int rectangle(int x,int y);
寫在main()函數之內很傻

(應該把函數類型聲明)寫在函數外面

另外那個temp多余

temp賦值為零就更多余

(因為可以直接)

sum += (x-i+1)*(y-j+1);    

  就這樣,他再次進行了修改:

原代碼-3

#include <stdio.h>
int rectangle(int x,int y)
{
    int i,j,sum,temp;

    for(i=1,sum=0;i<=x;i++)
    {
        for(j=1;j<=y;j++)
        {
            sum += (x-i+1)*(y-j+1);;
        }
    }

    return sum;
}

int rectangle(int x,int y);
int main()
{
    long int A,B;
    printf("input A&B and end with EOF\n");

    while(scanf("%d%d",&A,&B)!= EOF)
    {
        printf("%d\n",rectangle(A,B));
    }

    return 0;
}

 評析:

   現在毛病少多了,不過還是有一些。

  首先, rectangle()函數定義的位置不妥,寫在main()定義的后面為好。

  其次,數據類型有問題,這個問題比較嚴重。 

    long int A,B;

  這里絕對沒有必要把A、B定義為long類型,int類型足矣。由於不當地把A、B定義成了long類型,所以代碼中的

scanf("%d%d",&A,&B)

rectangle(A,B)

這兩次調用都是錯的。雖然沒有產生錯誤的結果(估計在那個系統中long和int類型大小一樣),但其實是瞎貓碰到死耗子,僥幸而已。

  此外的兩處小瑕疵就是rectangle()函數定義中的temp變量忘記刪除了,循環體內刪除語句時不干凈,有一“;”也忘記刪除了。

  再有就是 

sum += (x-i+1)*(y-j+1);

   這個算法我沒看懂,不知道是否正確。

重構:

  其實原來代碼已經改得差不多了,只剩下少許小錯和瑕疵。我在這里只說一下我的算法:

  我的算法是,窮舉出兩個點(P1,P2)的所有組合情況,只要P1可以是某個矩形的左上角,P2可以是某個矩形的右下角(P1_X<P2_X,P1_Y<P2_Y),則構成了一個矩形。

 1 /*
 2 矩形的個數 
 3 在一個3*2的矩形中,可以找到6個1*1的矩形,4個2*1的矩形3個1*2的矩形,
 4 2個2*2的矩形,2個3*1的矩形和1個3*2的矩形,總共18個矩形。 
 5 給出A,B,計算可以從中找到多少個矩形。 
 6 
 7 輸入: 
 8 本題有多組輸入數據(<10000),你必須處理到EOF為止 
 9 輸入2個整數A,B(1<=A,B<=1000) 
10 
11 輸出: 
12 輸出找到的矩形數。 
13 
14 樣例:
15 
16 輸入: 
17 1 2 
18 3 2 
19 
20 輸出: 
21 3 
22 18
23 
24 作者:薛非
25 出處:http://www.cnblogs.com/pmer/   “C語言初學者代碼中的常見錯誤與瑕疵”系列博文 
26 
27 */
28 
29 #include <stdio.h>
30 
31 int count( int , int ); 
32 
33 int main( void )
34 {
35   int A , B ;
36   
37   while ( printf( "輸入2個整數A,B(1<=A,B<=1000)" ),
38           scanf( "%d%d" , &A , &B )!= EOF    
39         )
40   {
41      printf( "%d\n" , count( A , B ) );
42   }
43   
44   return 0;
45 }
46 
47 int count( int A , int B )
48 {
49    int x1 , y1 ;//第一個點的坐標 
50    int x2 , y2 ;//第二個點的坐標
51    int num = 0 ;
52    
53    for ( x1 = 0 ; x1 <= B ; x1 ++ )
54       for ( y1 = 0 ; y1 <= A ; y1 ++ )//窮舉第一個點的各種可能 
55          for ( x2 = 0 ; x2 <= B ; x2 ++ )
56             for ( y2 = 0 ; y2 <= A ; y2 ++ )//窮舉第二個點的各種可能 
57             {
58                if ( x1 < x2 && y1 < y2 )
59                   num ++ ;
60             }
61 
62    return num ;
63 }

 BUG

  這個代碼的不足之處在於沒有認真思考答案是否在int類型的表示范圍之內。在 飛鳥_Asuka 網友提出是否“時間復雜度比較大”的問題后,我一並考慮了這兩個問題。結論是:1.這個問題用數學的辦法很容易解決,不過采用這種方案對學習編程是不利的,因為求解太容易了;2.答案確實有可能超過int類型的表示范圍(假如int最大能表示到231-1的話)。這應該算是重構代碼中存在的一個BUG。我將在以后的博文中給出修正及數學解答。

  在此感謝飛鳥_Asuka 網友的提醒。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM