2021湖南省赛 洞穴探宝 题解(状压dp)

题目链接

题目大意

题目思路

比赛的时候读错题了,以为是总共只能去x一次,后面发现是每个x点只能去一次

后面比赛的时候想的是\(dp[x][y][sta1][sta2]\)

表示现在位于\((x,y)\)节点,且踩了\(sta1\)状态的陷阱,有\(sta2\)状态的宝藏

显然复杂度过大

后面看了题解,其实不需要记录\(x,y\)因为很多点本质上是一样的,只需要记录现在位于哪个特殊节点即可

把起点和“X”和“@”当作结点建图,边为通过“.”能够直接连通的结点。以“当前结点”+“遇到过的结点状态压缩”为状

态DP。

写起来感觉很麻烦,感觉自己写的有点绕

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=100+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,m;
char s[maxn][maxn];
int mp[maxn][maxn],vis[maxn][maxn];
int ans[30];
int cnt;
int dp[1<<21][22];
vector<int> vec[30],gt[30];
pair<int,int> pa[30];
int dx[]={0,-1,1,0,0};
int dy[]={0,0,0,-1,1};
void init(){// 标号
    for(int i=1;i<=20;i++){
        ans[i]=0;
        gt[i].clear();
    }
    cnt=1;
    for(int j=1;j<=m;j++){// 找出口标记为1
        if(s[1][j]=='.'){
            mp[1][j]=1;
        }
        if(s[n][j]=='.'){
            mp[n][j]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(s[i][1]=='.'){
            mp[i][1]=1;
        }
        if(s[i][m]=='.'){
            mp[i][m]=1;
        }
    }
    //给陷阱和宝藏标号
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(s[i][j]=='X'||s[i][j]=='@'){
                mp[i][j]=++cnt;
            }
            if(s[i][j]=='@'){
                ans[cnt]=1;
            }
            if(mp[i][j]){
                pa[mp[i][j]]={i,j};
            }
        }
    }
    for(int i=1;i<=n;i++){ // 标记每个点能领到什么宝藏
        for(int j=1;j<=m;j++){
            if(mp[i][j]==0) continue;
            for(int k=0;k<=4;k++){
                int nx=i+dx[k];
                int ny=j+dy[k];
                if(nx<1||nx>n||ny<1||ny>m){
                    continue;
                }
                if(s[nx][ny]=='@'){ // 放进去之后就不走了
                    gt[mp[i][j]].push_back(mp[nx][ny]);
                }
            }
        }
    }
}
void bfs(int id,int x,int y){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            vis[i][j]=0;
        }
    }
    queue<pair<int,int> > que;
    que.push({x,y});
    vis[x][y]=1;
    while(!que.empty()){
        pii now=que.front();
        que.pop();
        for(int i=1;i<=4;i++){
            int nx=now.fi+dx[i];
            int ny=now.se+dy[i];
            if(nx<1||nx>n||ny<1||ny>m||vis[nx][ny]||s[nx][ny]=='#'){
                continue;
            }
            vis[nx][ny]=1;
            if(mp[nx][ny]){ // 放进去之后就不走了
                vec[id].push_back(mp[nx][ny]);
            }else{
                que.push({nx,ny});
            }
        }
    }
}
int cal(int x){
    int cnt=0;
    while(x){
        if(x&1) cnt++;
        x/=2;
    }
    return cnt;
}
signed main(){
    while(scanf("%d%d",&n,&m)!=-1){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf(" %c",&s[i][j]);
                mp[i][j]=0;
            }
        }
        init();
        for(int i=1;i<=cnt;i++){
            vec[i].clear();
            bfs(i,pa[i].fi,pa[i].se);
        }
        for(int sta=1;sta<(1<<cnt);sta++){
            for(int now=0;now<cnt;now++){
                dp[sta][now]=0;
            }
        }
        dp[1][0]=1;
        for(int sta=1;sta<(1<<cnt);sta++){
            for(int now=0;now<cnt;now++){
                if(dp[sta][now]==0) continue;
                for(auto x:vec[now+1]){
                    int nxt=x-1;
                    // 如果走过并且是x点,则不再走
                    if((sta&(1<<nxt))&&s[pa[x].fi][pa[x].se]=='X') continue;
                        dp[sta|(1<<nxt)][nxt]=1;
                }
            }
        }
        int pr=0;
        for(int sta=1;sta<(1<<cnt);sta++){
            if(dp[sta][0]==0) continue;
            int tmp=0;
            int now=0;
            for(int j=1;j<=cnt;j++){
                if(sta&(1<<(j-1))){
                    for(auto x:gt[j]){
                        now|=(1<<x);
                    }
                }
            }
            pr=max(pr,cal(now));
        }
       printf("%d\n",pr);
    }
    return 0;
}
// 3 1 3 2

不摆烂了,写题
原文地址:https://www.cnblogs.com/hunxuewangzi/p/15802004.html