PAT顶级(状态压缩记忆化搜索)——1019 Separate the Animals (35 分)

本文探讨了一种二维地图上的优化搜索算法,用于解决在满足特定条件(如障碍物4-连通性、动物不相遇、特定数量的洞穴)下放置障碍物的问题。作者首先尝试了暴力搜索,但因复杂度过高导致超时。接着,通过状态压缩和记忆化搜索实现了更高效的解决方案,减少了重复计算。在每一步中,算法会尝试在空地上放置障碍,并通过深度优先搜索判断是否符合要求。最终,算法能够正确判断是否存在合法的障碍物布局,满足题目所给的洞穴数量。
摘要由CSDN通过智能技术生成

1019 Separate the Animals (35 分)


解题思路:

差点就想放弃了,之前没写过类似的搜索,写了一个最暴力的,怎么剪枝优化都是T。

第一种思路——好想,但是复杂度过大,能拿23分:

DFS搜索每个空地的状态,对于每一个情况,judge判断是否合法,判断内容包括1.所有障碍是否4-connected;2.是否有动物会遇到一起;3.是否有h个hole。其中2和3用bfs跑连通分量可以一起实现。

第二种思路:

枚举每一个空地,在该位置放下一个障碍,然后开始DFS:每次都对已有的障碍都进行往四个方向进行扩展的进一步DFS。
显而易见,DFS的情况数巨大无比,同时这里使用状态压缩——将二维数组压缩为一维,然后对应位是障碍就是1,否则就是0,显然,这样就让每一个状态映射为一个唯一的二进制数,然后使用记忆化搜素,对于搜索过的状态直接return;这样还可以去重。
最后进行judge,与第一种思路的类似,还省去了第一部分的judge内容。

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 1e9 + 7;
const int MAXN = 30000005;
const int MAX2 = 300005;
inline ll read() {
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
ll fpow(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
int n, m;
int k, h;
int vis[20][20];
string s[20];
int g[20][20];
int nx[4] = { 1,0,-1,0 };
int ny[4] = { 0,1,0,-1 };
pii fa[20][20];
bool judge()
{
    memset(vis, 0, sizeof vis);
    int hole = 0;
    int animals = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            fa[i][j] = mp(i, j);
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (!vis[i][j] && g[i][j] != 2)
            {
                animals = 0;
                int flag = 0;
                queue<pii> q;
                q.push(mp(i, j));
                vis[i][j] = 1;
                while (q.size())
                {
                    auto p = q.front();
                    q.pop();
                    if (g[p.first][p.second] == 1)animals++;
                    if (p.first == 1 || p.first == n || p.second == 1 || p.second == m)
                    {
                        //走到了边界(即该情况的连通分量不是一个hole)
                        flag = 1;
                    }
                    for (int k = 0; k < 4; k++)
                    {
                        int x = nx[k] + p.first, y = ny[k] + p.second;
                        if (x >= 1 && x <= n && y >= 1 && y <= m && !vis[x][y] && g[x][y] != 2)
                        {
                            q.push(mp(x, y));
                            vis[x][y] = 1;
                        }
                    }
                }
                if (!flag)hole++;
                if (animals >= 2)return false;
                //多次遇到动物;
            }
        }
    }
    return hole == h;
}
int ans = 0;
bool valid(int x, int in)
{
    return x >= 1 && x <= in;
}
unordered_map<ll, int> um;
void dfs(int i, int j, int now,ll value)
{
    if (um[value])return;
    um[value] = 1;
    if (now == k)
    {
        if (judge())
        {
            ans++;
        }
        return;
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (g[i][j] == 2)
            {
                for (int k = 0; k < 4; k++)
                {
                    int x = nx[k] + i, y = ny[k] + j;
                    if (!g[x][y] && valid(x, n) && valid(y, m))
                    {
                        g[x][y] = 2;
                        dfs(x, y, now + 1, 1ll << (x * m + y) | value);
                        g[x][y] = 0;
                    }
                }
            }
        }
    }
    
}
signed main()
{
    n = rd, m = rd, k = rd, h = rd;
    int cnt = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> s[i];
        for (int j = 0; j < s[i].size(); j++)
        {
            if (s[i][j] == 'O')
            {
                g[i + 1][j + 1] = 1;
                cnt++;
            }
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (!g[i][j])
            {
                g[i][j] = 2;
                dfs(i, j, 1,1ll<<(i*m+j));
                g[i][j] = 0;
            }
        }
    }
    cout << ans << endl;
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值