題目
思路分析
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;
}