[XSY 1176][状压dp]大包子环绕宝藏

我们考虑状压dp,令fi,j,k表示当前位于(i,j),k集合中的点与当前边有奇数交
而k集合外的点与当前边有偶数交的最小步数
bfs一遍可以得到
最后枚举合法的集合
时间复杂度:
O ( n m k 2 k ) O(nmk2^k) O(nmk2k

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 22
char ch[Maxn][Maxn];
struct P{
    int x,y;
}p[10];
int cnt=0;
int w[10];
int f[Maxn][Maxn][256],Q[Maxn*Maxn*256];
int hd,tl;
int nx[4]={0,0,1,-1};
int ny[4]={1,-1,0,0};
inline int merge(int x,int y,int z){return x*Maxn*(1<<cnt)+y*(1<<cnt)+z;}
inline int getx(int num){return num/Maxn/(1<<cnt);}
inline int gety(int num){return num/(1<<cnt)%Maxn;}
inline int getz(int num){return num%(1<<cnt);}
inline bool Judge(int x,int y){
    if(x<1||x>n||y<1||y>m)return false;
    return ch[x][y]=='.';
}
int main(){
    scanf("%d%d",&n,&m);
    int sx,sy;
    for(register int i=1;i<=n;++i)scanf("%s",ch[i]+1);
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)
            if(ch[i][j]=='S'){
                sx=i;sy=j;
                ch[i][j]='.';
            }else if(ch[i][j]>'0'&&ch[i][j]<'9'){
                p[ch[i][j]-'0'-1]=(P){i,j};
                cnt=max(cnt,ch[i][j]-'0');
                ch[i][j]='#';
            }
    int pre=cnt;
    for(register int i=0;i<cnt;++i)scanf("%d",&w[i]);
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)
            if(ch[i][j]=='B'){
                p[cnt++]=(P){i,j};
                ch[i][j]='#';
            }
    memset(f,63,sizeof(f));
    f[sx][sy][0]=0;
    hd=tl=0;
    Q[tl++]=merge(sx,sy,0);
    while(hd<tl){
        int x=getx(Q[hd]);
        int y=gety(Q[hd]);
        int z=getz(Q[hd]);
        hd++;
        for(int i=0;i<4;++i)
            if(Judge(x+nx[i],y+ny[i])){
                int nz=z;
                if(i==0)
                    for(int j=0;j<cnt;++j)
                        if(p[j].x>x&&p[j].y==y)
                            nz^=(1<<j);
                if(i==1)
                    for(int j=0;j<cnt;++j)
                        if(p[j].x>x&&p[j].y==y-1)
                            nz^=(1<<j);
                if(f[x][y][z]+1<f[x+nx[i]][y+ny[i]][nz]){
                    f[x+nx[i]][y+ny[i]][nz]=f[x][y][z]+1;
                    Q[tl++]=merge(x+nx[i],y+ny[i],nz);
                }
            }
    }
    int Ans=0;
    for(int i=0;i<1<<cnt;++i){
        int ans=0;
        bool valid=true;
        for(int j=0;j<cnt;++j){
            int tmp=i&(1<<j);
            if(j>=pre&&tmp)valid=false;
            if(j<pre&&tmp)ans+=w[j];
        }
        if(!valid)continue;
        Ans=max(Ans,ans-f[sx][sy][i]);
    }
    printf("%d\n",Ans);
    return 0;
}/*
7 7
.......
.1###2.
.#...#.
.#.B.#.
.3...4.
..##...
......S
100
100
100
100
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值