方格分割 二進位制列舉+DFS(2017 第八屆藍橋杯省賽A組 第4題)

paplion發表於2019-02-22

標題:方格分割

6x6的方格,沿著格子的邊線剪開成兩部分。
要求這兩部分的形狀完全相同。

如圖:p1.png, p2.png, p3.png 就是可行的分割法。

試計算:
包括這3種分法在內,一共有多少種不同的分割方法。
注意:旋轉對稱的屬於同一種分割法。

請提交該整數,不要填寫任何多餘的內容或說明文字。

p1.png

由於影象是中心對稱,所以我們窮舉左邊的18個方格,共有2^18=262144種可能的情況,窮舉出左邊,通過對稱再得到右邊18個方格的情況,從而得到整附圖:

 for(int i=0;i < (1<<18);++i)   //i=0~2^18-1
    {
        for(int l=0;l<6;l++)
        {
            for(int c=0;c < 3;c++)
            {
                if(i&(1<<cnt))   //把i的每一位取出填入圖形
                {
                    mp[l][c] = 1;     //填充左半部分  1代表橘色  0代表黃色
                    mp[5-l][5-c] = 0;  //對稱得到右半部分
                }else
                {
                    mp[l][c] = 0;   
                    mp[5-l][5-c] = 1;
                }
            
            }
        }

然後對填充後的圖形進行連通性檢測,如果填充後的圖形存在18個方塊的連通域則符合要求,方案數加一,用DFS檢測連通:

int vis[6][6];
int dx[8]={1, 0, 0,-1};
int dy[8]={0,-1, 1, 0};
int node_cnt;

int dfs(int x, int y)
{
    
    for(int i=0;i < 4;i++)     //從當前方格的上,下,左,右進行搜尋
    {
        int nx = x + dx[i];   
        int ny = y + dy[i];
        if(node_cnt==18) return node_cnt;   //已找到18個連通的方塊,則進行剪枝
        if(0<=nx && nx < 6 && 0<=ny && ny <6)
        {
            if(mp[nx][ny]==mp[x][y]&&vis[nx][ny]==0)  //找到同一區域方塊
            {
                vis[nx][ny]=1;   //,標記
                node_cnt++;    //方塊數加1
                dfs(nx,ny);
            }
        }
    }
    
    
    return node_cnt;

以下是完整程式碼:

#include <iostream>
#include <cstring>
using namespace std;

int mp[6][6];
int vis[6][6];
int dx[8]={1, 0, 0,-1};
int dy[8]={0,-1, 1, 0};
int node_cnt;

void printmp()
{
	for(int i=0; i < 6;i++)
	{
		for(int j=0 ;j < 6;j++)
		{
			cout<<mp[i][j]<<" ";
		}
		cout<<endl;
	}
}


int dfs(int x, int y)
{
	
	for(int i=0;i < 4;i++)
	{
		int nx = x + dx[i];
		int ny = y + dy[i];
		if(node_cnt==18) return node_cnt;
		if(0<=nx && nx < 6 && 0<=ny && ny <6)
		{
			if(mp[nx][ny]==mp[x][y]&&vis[nx][ny]==0)
			{
				vis[nx][ny]=1;
				node_cnt++;	
				dfs(nx,ny);
			}
		}
	}
	
	
	return node_cnt;
} 

int main()
{
    int ans=0;
	for(int i=0;i < (1<<18);++i)
	{
		int cnt=0;
		for(int l=0;l<6;l++)
		{
			for(int c=0;c < 3;c++)
			{
				if(i&(1<<cnt))
				{
					mp[l][c] = 1;
					mp[5-l][5-c] = 0;
				}else
				{
					mp[l][c] = 0;
					mp[5-l][5-c] = 1;
				}
				cnt++;
			}
		}
		memset(vis,0,sizeof(vis));
		node_cnt = 1;
		vis[0][0] = 1;
		if(dfs(0,0)==18) ans++;
//		printmp();
//		cin>>cnt;
	}
	cout<<ans/4;   //中心對稱算一種,最後還要除以4
	return 0;
} 

最終答案為:509

相關文章