围豆豆

是不是平时在手机里玩吃豆豆游戏玩腻了呢?最近MOKIA手机上推出了一种新的围豆豆游戏,大家一起来试一试吧。

游戏的规则非常简单,在一个N×M的矩阵方格内分布着D颗豆子,每颗豆有不同的分值Vi。游戏者可以选择任意一个方格作为起始格,每次移动可以随意的走到相邻的四个格子,直到最终又回到起始格。最终游戏者的得分为所有被路径围住的豆豆的分值总和减去游戏者移动的步数。矩阵中某些格子内设有障碍物,任何时刻游戏者不能进入包含障碍物或豆子的格子。游戏者可能的最低得分为0,即什么都不做。

注意路径包围的概念,即某一颗豆在路径所形成的多边形(可能是含自交的复杂多边形)的内部。下面有两个例子:
1.gif2.gif
第一个例子中,豆在路径围成的矩形内部,所以豆被围住了。第二个例子中,虽然路径经过了豆的周围的8个格子,但是路径形成的多边形内部并不包含豆,所以没有围住豆子。
布布最近迷上了这款游戏,但是怎么玩都拿不了高分。聪明的你决定写一个程序来帮助他顺利通关。

输入格式:
输入第一行两个整数N和M,为矩阵的边长。
第二行一个整数D,为豆子的总个数。
第三行包含D个整数V1到VD,分别为每颗豆子的分值。
接着N行有一个N×M的字符矩阵来描述游戏矩阵状态,0表示空格,#表示障碍物。而数字1到9分别表示对应编号的豆子。

输出格式:
输出仅包含一个整数,为最高可能获得的分值。

样例输入:
3 8
3
30 -100 30
00000000
010203#0
00000000

样例输出:
38

数据范围:
50%的数据满足1≤D≤3。
100%的数据满足1≤D≤9,1≤N, M≤10,-10000≤Vi≤10000。

时间限制:
1s

空间限制:
512M

本题是非常恶心的状压广搜,建议重新打一遍,我们知道如果他两次经过一个点的上面,那他一定没有围住这个点(还有一种情况是他在上面向上走了一会又掉头回去了,这就是取min的原因)仅经过奇数次说明改点被围住。所以就据此状态压缩dp,第一次走到该状态即为最优。

#include<bits/stdc++.h>
using namespace std;
int n,m;
char xx[101][101],tab;
int dp[20][20][1024];
int lo[20][20],d,val[101];
int pg[20][20];
int vis[20],ans;
bool used[20][20][1024];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
struct NODE{
    int x,y,se;
};
int cal(int xx)
{
    int aa=0;
    for(int i=0;i<d;i++)
    {
        int dd=1<<(i),num=i+1;
        if(dd&xx) aa+=val[num];
    }
    return aa;
}
queue<NODE> q;
int calc(int xx,int yy)
{
    return cal(yy)-cal(xx);
}
void init()
{
    memset(dp,-20,sizeof(dp));
    memset(used,0,sizeof(used));
    while(!q.empty()) q.pop();
}
void doit(int x,int y)
{
    init();
    NODE ind,pus;
    ind.x=x;ind.y=y;ind.se=0;
    used[x][y][0]=1;dp[x][y][0]=0;
    q.push(ind);
    while(!q.empty())
    {
        ind=q.front();q.pop();
        for(int ty=0;ty<4;ty++)
        {
            int sx=dx[ty]+ind.x;
            int sy=dy[ty]+ind.y;
            int s=ind.se;
            if(pg[sx][sy]||sx>n||sx<1||sy>m||sy<1) continue;
            int h=dp[ind.x][ind.y][ind.se]-1;
            int sel=s;
            if(dy[ty]!=0)
            {
                int u=min(sy,ind.y);
                sel=s^lo[sx][u];
                h=h+calc(s,sel);
            }
            if(!used[sx][sy][sel])//广搜,第一次搜到的就是最优的
            {
                dp[sx][sy][sel]=h;
                used[sx][sy][sel]=1;
                pus.x=sx;pus.y=sy;pus.se=sel;
                q.push(pus);
            }
        }
    }
    for(int i=0;i<(1<<d);i++) if(used[x][y][i]) ans=max(ans,dp[x][y][i]);
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%d",&d);
    for(int i=1;i<=d;i++) scanf("%d",&val[i]);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",xx[i]+1);
    } 
//  for(int i=1;i<=n;i++){cout<<endl;for(int j=1;j<=m;j++)cout<<xx[i][j]; }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(xx[i][j]>='1'&&xx[i][j]<='9')
            {
                int num=xx[i][j]-'0';
                pg[i][j]=1;
//              cout<<num<<" "<<(1<<(num-1))<<endl;
                for(int k=i-1;k>=1;k--) lo[k][j]+=1<<(num-1);
            }
            else if(xx[i][j]=='#') pg[i][j]=1;
        }
    }
//  for(int i=1;i<=n;i++)
//  {
//      puts("");
//      for(int j=1;j<=m;j++)
//      cout<<lo[i][j]<<" ";
//  }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        if(!pg[i][j])doit(i,j);
    }
    cout<<ans;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值