题目
是不是平时在手机里玩吃豆豆游戏玩腻了呢?最近MOKIA手机上推出了一种新的围豆豆游戏,大家一起来试一试吧。
游戏的规则非常简单,在一个N×M的矩阵方格内分布着D颗豆子,每颗豆有不同的分值Vi。游戏者可以选择任意一个方格作为起始格,每次移动可以随意的走到相邻的四个格子,直到最终又回到起始格。最终游戏者的得分为所有被路径围住的豆豆的分值总和减去游戏者移动的步数。矩阵中某些格子内设有障碍物,任何时刻游戏者不能进入包含障碍物或豆子的格子。游戏者可能的最低得分为0,即什么都不做。
注意路径包围的概念,即某一颗豆在路径所形成的多边形(可能是含自交的复杂多边形)的内部。下面有两个例子:
第一个例子中,豆在路径围成的矩形内部,所以豆被围住了。第二个例子中,虽然路径经过了豆的周围的8个格子,但是路径形成的多边形内部并不包含豆,所以没有围住豆子。
布布最近迷上了这款游戏,但是怎么玩都拿不了高分。聪明的你决定写一个程序来帮助他顺利通关。
Input
第一行两个整数N和M,为矩阵的边长。 第二行一个整数D,为豆子的总个数。 第三行包含D个整数V1到VD,分别为每颗豆子的分值。 接着N行有一个N×M的字符矩阵来描述游戏矩阵状态,0表示空格,#表示障碍物。而数字1到9分别表示对应编号的豆子。
Output
仅包含一个整数,为最高可能获得的分值。
Sample Input
3 8
3
30 -100 30
00000000
010203#0
00000000
Sample Output
38
Hint
50%的数据满足1≤D≤3。
100%的数据满足1≤D≤9,1≤N, M≤10,-10000≤Vi≤10000。
解说
这道题看着很恶心,做起来更恶心。
首先我试着拿DP的方式来思考:每个豆子我可以选可以不选,围住一个豆子至少需要8的格的代价,也就是说如果围了这个豆子的得分比8大的话我就去围它,而一个点跑到另一个点的时候就计算下路程,另一个点带来的价值比路程大的话就去围它。对于负数肯定是尽量不围,但是如果绕过它用的价值比围它亏损的还大那就围它……
但是,特殊情况好多啊。比如几个豆子连在一起的话围它们需要的平均价值就不是8了,还有,不能走的格子无法被纳入这个体系,哦还有究竟什么算是“绕过负的格子”……这么复杂的话我还不如研究博弈论呢……
总之,这个思路肯定不对。
到最后我真的想不出来了……看看题解吧……
前置知识
如何判断一个点是否在一个多边形内部?从这个点向外引一条射线,若与多边形相交了奇数次,就在它的内部,否则在外部。
正解
d很小,考虑状压DP。
预处理出豆子的坐标和每个状态下所有豆子的得分和sum[S]。
首先枚举一个起点(x,y)。设f[i][j][S]表示走到了(i,j)这个格子,当前圈住的豆子的状态为S的最小边界长度。
显然这个东西可以跑一遍最短路得到,具体实现还是见代码。
然后,枚举状态S。因为要走一条回路,所以用sum[S]−f[x][y][S]来更新答案。
最后输出答案,然后就做完啦。
——引自M_sea's Blog
所以,就这样,状压DP,刚学还不熟练,没有想到。
代码
1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #include <cmath> 6 #include <queue> 7 using namespace std; 8 9 inline int read() { 10 int X=0,w=1; char c=getchar(); 11 while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); } 12 while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar(); 13 return X*w; 14 } 15 16 const int fx[4][2]={1,0,-1,0,0,1,0,-1}; 17 18 int n,m,d,ans=-2e9; 19 int f[15][15][1<<9]; //f[i][j][S]表示当前在(i,j),围住的豆豆状态为S的最小边界长度 20 int sum[1<<9]; //sum[S]表示S状态的总分 21 char g[15][15]; 22 int val[15],X[15],Y[15]; 23 24 struct node { int x,y,S; }; 25 int dp[15][15][1<<9]; 26 27 28 int get(int x,int y,int xx,int yy,int S) { 29 for (int i=0;i<d;++i) 30 if (((x==X[i]&&xx>X[i])||(x>X[i]&&xx<=X[i]))&&yy>Y[i]) S^=1<<i; 31 return S; 32 } 33 34 inline void solve(int x,int y) { 35 queue<node> Q; 36 Q.push((node){x,y,0}); 37 memset(f,0x3f,sizeof(f)); 38 f[x][y][0]=0,dp[x][y][0]=1; 39 while (!Q.empty()) { 40 node fr=Q.front(); Q.pop(); 41 int x=fr.x,y=fr.y,S=fr.S; dp[x][y][S]=0; 42 for (int i=0;i<4;++i) { 43 int X=x+fx[i][0],Y=y+fx[i][1]; 44 if (g[X][Y]!='0') continue; 45 int SS=i<2?get(x,y,X,Y,S):S; 46 if (f[x][y][S]+1<f[X][Y][SS]) { 47 f[X][Y][SS]=f[x][y][S]+1; 48 if (!dp[X][Y][SS]) { 49 dp[X][Y][SS]=1; 50 Q.push((node){X,Y,SS}); 51 } 52 } 53 } 54 } 55 for (int S=0;S<(1<<d);++S) 56 ans=max(ans,sum[S]-f[x][y][S]); 57 } 58 59 int main() { 60 n=read(),m=read(),d=read(); 61 for (int i=0;i<d;++i) val[i]=read(); 62 for (int i=1;i<=n;++i) scanf("%s",g[i]+1); 63 for (int i=0;i<=n+1;++i) g[i][0]=g[i][m+1]='#'; 64 for (int i=0;i<=m+1;++i) g[0][i]=g[n+1][i]='#'; 65 for (int S=0;S<(1<<d);++S) 66 for (int i=0;i<d;++i) 67 if (S&(1<<i)) sum[S]+=val[i]; 68 for (int i=1;i<=n;++i) 69 for (int j=1;j<=m;++j) 70 if (g[i][j]>='1'&&g[i][j]<='9') 71 X[g[i][j]-'1']=i,Y[g[i][j]-'1']=j; 72 for (int i=1;i<=n;++i) 73 for (int j=1;j<=m;++j) 74 if (g[i][j]=='0') solve(i,j); 75 printf("%d ",ans); 76 return 0; 77 }
幸甚至哉,歌以咏志。