Codeforces 375C Circling Round Treasures 题解

题意

给你一个N*M的地图(N,M<=20),地图上’#’表示障碍物,’B’表示炸弹,数字表示宝藏序号(宝藏+炸弹个数 <=8),每个宝藏有价值(-200<=v<=200),’S’表示出发点。每次移动可以从一个格子移动到相邻格子(上下左右)。寻找一条路径从’S’出发再回到’S’的封闭路径,移动步数记为K,这个路径所包围的宝藏价值总和记为V,则这条路径的价值为V-K。题目即是求可行的最大的路径价值,并且不能包围炸弹。

题解

可以将炸弹转化为v=-1600的宝藏,这样可确保选不到炸弹。

状压一下选的宝藏,之后便可以跑dp。

那么怎么确定选中了哪些宝藏呢?可以运用射线法(很好理解、但之前不知道0.0)。

射线法

可以从点向任意方向发出一条射线,判断与这个多边形的交点数量的奇偶性,奇数则在内,偶数则在外。但要注意一下与顶点相交时的判断。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
bool can[30][30];
int sx,sy;
struct state{
    int x,y,st;
};
int x[10],y[10],w[10],mp[10];int tot=0;
int bx[10],by[10];int btot=0;
int dp[30][30][256];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int sum[256];
int main(){
    scanf("%d %d\n",&n,&m);
    char ch;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            scanf("%c",&ch);
            switch(ch){
                case '.':
                case 'S':
                    can[i][j]=1;break;
                default:
                    can[i][j]=0;
            }
            switch(ch){
                case '#':
                case '.': break;
                case 'S':
                    sx=i;sy=j;break;
                case 'B':
                    bx[++btot]=i;
                    by[btot]=j;
                    break;
                default:
                    x[++tot]=i;
                    y[tot]=j;
                    mp[ch-'1'+1]=tot;
                    break;
            }
        }
        scanf("\n");
    }
    for(int i=1;i<=tot;++i){
        scanf("%d",&w[mp[i]]);
    }
    for(int i=1;i<=btot;++i){
        x[++tot]=bx[i];
        y[tot]=by[i];
        w[tot]=-3000;
    }
    for(int i=1;i<(1<<tot);++i){
        for(int j=0;j<tot;++j){
            if(i&(1<<j))sum[i]+=w[j+1];
        }
    }
    memset(dp,0xff,sizeof dp);
    dp[sx][sy][0]=0;
    queue<state> q;
    q.push({sx,sy,0});
    int ans=0;
    while(q.size()){
        state u = q.front();q.pop();
        if(u.x==sx&&u.y==sy){
            ans=max(ans,sum[u.st]-dp[u.x][u.y][u.st]);
        }
        for(int i=0;i<4;++i){
            int tx=u.x+dx[i],ty=u.y+dy[i];
            if(tx<1||tx>n||ty<1||ty>m)continue;
            if(!can[tx][ty])continue;
            int nst=u.st;
            for(int j=1;j<=tot;++j){
                if ((x[j]==tx&&u.x<tx&&y[j]<ty) //相当于是向右边发射了一条射线,nst就表示了奇偶性
                ||  (x[j]==u.x&&u.x>tx&&y[j]<u.y))nst^=1<<(j-1);
            }
            if(!(~dp[tx][ty][nst])){
                dp[tx][ty][nst]=dp[u.x][u.y][u.st]+1;
                q.push({tx,ty,nst});
            }
        }
    }
    printf("%d",ans);
}