acwing算法學習——疑難雜題——剪格子

題目

在這裏插入圖片描述

在這裏插入圖片描述

思路分析

1.首先判斷有沒有 聯通塊
2.然後搜索(⚠️ 這裏不是搜索一筆畫畫完的那種
3.DFS
4.枚舉每一個dfs的點的四個方向,都放入s當中
5.再進行一次DFS
6.注意判重,搜過的就不再搜了

搜索方式

在這裏插入圖片描述

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<unordered_set>  //生成哈希
#include<vector>

#define x first
#define y second

using namespace std;

typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N = 10,INF = 1e8,P = 131;  //這裏的P做哈希的時候用,一般是P進制的數,一般取131 或者 13331

int n,m;  //n行 m列
int w[N][N];  //存放整個表格
bool st[N][N];  //表示是否被標記過
int sum,ans = INF;
PII cands[N*N];  //存儲現在在就緒隊列裏的點的座標
int p[N*N];  //並查集的時候用

unordered_set<ULL> hash_table;  //生成哈希

int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};  //四個方向

int find(int x)  //find函數固定套路
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

bool check_connect(int k)  //檢查剩餘的部分是不是連通塊  k  表示現在用了多少塊
{
    for(int i = 0;i < n * m;i++) p[i] = i;  //p[i] 的初始化  ,並查集
    int cnt = n * m - k;  //剩下多少塊
    
    for(int i = 0;i < n;i++)
        for(int j = 0;j < m;j++)
        if(!st[i][j])  //沒有遍歷過
        {
            for(int u = 0;u < 4;u++)
            {
                int a  = i + dx[u],b = j + dy[u];
                if(a < 0 || a >= n || b < 0 || b >= m) continue;
                if(st[a][b]) continue;  //已經遍歷過
                
                int p1 = find(i * m + j),p2 = find(a * m + b);
                if(p1 != p2)  //不在一個樹裏面
                {
                    p[p1] = p2;  //那就放到一個樹裏面
                    cnt--;
                }
            }
        }
    
    if(cnt != 1) return false;
    return true;
}

bool check_exists(int k)  //檢查現在的連通塊是否之前已經搜索過
{
    static PII bk[N * N];
    for(int i = 0;i < k;i++) bk[i] = cands[i];
    
    sort(bk,bk + k);
    
    ULL x = 0;
    for(int i = 0; i < k;i++)
    {
        x = x * P + bk[i].x + 1;
        x = x * P + bk[i].y + 1;
    }
    
    if(hash_table.count(x)) return true;  
    hash_table.insert(x);  //否則就插入哈希值
    
    return false;
}


void dfs(int s,int k)  //s 現有的點的值的和   k  現在有幾個點
{
    if(s  == sum / 2)
    {
        if(check_connect(k))
        {
            ans = min(ans,k);
        }
        return;
    }
    
    vector<PII> points;
    for(int i = 0;i < k;i++)
    {
        int x = cands[i].x,y = cands[i].y;
        for(int j = 0;j < 4;j++)
        {
            int a = x + dx[j],b = y + dy[j];
            if(a < 0 || a >= n || b < 0 || b >= m) continue;
            if(st[a][b]) continue;
            
            cands[k] = {a,b};  //把新擴展的點加進原來的點裏面
            if(k + 1 < ans && !check_exists(k + 1))
                points.push_back({a,b});
        }
    }
    
    sort(points.begin(),points.end());
    reverse(points.begin(),points.end());  //從大到小
    
    for(int i = 0; i < points.size();i++)  //在 待選的擴展點裏面找到合適的那一個
        if(!i || points[i] != points[i - 1])
        {
            cands[k] = points[i];
            int x = points[i].x,y = points[i].y;
            st[x][y] =  true;
            dfs(s + w[x][y],k + 1);
            st[x][y] = false;
        }
}

int main()
{
    cin >> m >> n;
    for(int i = 0;i < n;i++)
        for(int j = 0;j < m;j++)
        {
            cin >> w[i][j];
            sum += w[i][j];
        }
    if(sum % 2 == 0)
    {
        st[0][0] = true;
        cands[0] = {0,0};
        dfs(w[0][0],1);
    }
    
    if(ans == INF) ans =  0;
    cout << ans << endl;
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章