acwing算法学习——疑难杂题——剪格子

目录

  • 题目
  • 思路分析
        • 搜索方式
  • 代码

题目

acwing算法学习——疑难杂题——剪格子_第1张图片

acwing算法学习——疑难杂题——剪格子_第2张图片

思路分析

1.首先判断有没有 联通块
2.然后搜索(⚠️ 这里不是搜索一笔画画完的那种
3.DFS
4.枚举每一个dfs的点的四个方向,都放入s当中
5.再进行一次DFS
6.注意判重,搜过的就不再搜了

搜索方式

acwing算法学习——疑难杂题——剪格子_第3张图片

代码

#include
#include
#include
#include
#include  //生成哈希
#include

#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;
}




你可能感兴趣的:(算法学习)