動態規划-數正方形(詳解)


描述:

曉萌有一個N×N的的棋盤,中間有N*N個正方形的1×1的格子,他隨機在棋盤上撒上一些棋子(假設全部正好落在各個格子里)。他希望知道,當前的棋盤上有多少個不包含棋子的,由至少四個1×1的格子組成的正方形(正方形之間可以有重疊的部分)。

輸入第1行為棋盤的邊長N,第2行-第N+1組成一個每行有N個數字的棋盤,其中數字0表示這個格子內有棋子,1表示這個格子內沒有棋子。(2≤N≤250)

輸出包括多行,每行包括兩個用空格分隔的數字,分別表示可以找到的正方形的邊長和這種邊長的正方形的個數。

樣例輸入

6
101111
001111
111111
001111
101101
111001

樣例輸出

2 10         //2*2的正方形有10個
3 4          //3*3的正方形有4個
4 1         //4*4的正方形有1個

分析:

顯然這是個動態規划,若用暴力搜索,會在大數組上超時,所以需要每次保存上次的狀態

先來分析下2*2的正方形,如下圖:

從上圖可以看出,當在第s[i][j]數組上,且這個數組值為1(有棋子),那它會與之前地s[i-1][j-1]、s[i-1][j]、s[i][j-1]有關,若這3個任意一個都不滿足(為0)的話,那么s[i][j]就只能是個1*1正方形

再來深入分析,3*3的正方形,如下圖:

 從上圖可以看出,要想第s[i][j]數組上構成一個大於等於3*3的正方形,必須s[i-1][j-1]、s[i-1][j]、s[i][j-1]都至少是個2*2的正方形,比如上圖(3,3), 不然就會像上圖(2,2)那樣

從這里我們可以看出s[i][j]非0情況下, 會等於 這3個數組(s[i-1][j-1]、s[i-1][j]、s[i][j-1])的最小值+1

最后再分析一個有0數組,就迎刃而解了:

 

同樣地對於一個n*n正方形(n>=2),必定滿足(n-1)*(n-1)正方形、(n-2)*(n-2)正方形 ... ...

 代碼如下:

#include<stdio.h>
int s[300][300]={0},tp[300]={0}; int main() { int i,j,n,min=0,m=1,cnt; scanf("%d",&n); for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%1d",&s[i][j]); for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(s[i][j]!=0)   //第s[i][j]數組上,且這個數組值為1(有棋子)
 { min=(s[i-1][j]>s[i-1][j-1])?s[i-1][j-1]:s[i-1][j]; min=(s[i][j-1]>min)?min:s[i][j-1]; s[i][j]=min+1;               //等於 這3個數組(s[i-1][j-1]、s[i-1][j]、s[i][j-1])的最小值+1
        for(cnt=2;cnt<=min+1;cnt++) //對於一個n*n正方形(n>=2),必定滿足(n-1)*(n-1)正方形、(n-2)*(n-2)正方形 ... ...
        tp[cnt]++; } } for(i=2;i<=n;i++) if(tp[i]!=0) { if(m) { printf("%d %d",i,tp[i]); m=0; } else printf("\n%d %d",i,tp[i]); } return 0; } 


免責聲明!

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



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