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来着,又看了一眼数据范围感觉应该是状压,但是在那个路径围成的多边形里面如何判断呢?我学到了一种神奇的射线法,做法是这样的,从一点向随机方向引一条射线,如果射线和多边形的边相交奇数次,说明点在多边形内。那么我们真的需要用计算几何的知识来做射线判断?不需要,我们实际上这样写就可以了。
int segcross(int x, int y, int nx, int ny, int S)
{
for(int i=1; i<=D; i++){
if(((x<b[i].x && nx>=b[i].x) || (x>=b[i].x && nx<b[i].x)) && y > b[i].y){
S ^= 1<<(i-1);
}
}
return S;
}
为毛?我们射线是和方向有关的,如果我们坐标恰好也只限定一个方向刚好就满足了,可以在纸上画一下,接下来就是裸的SPFA了。
复杂度:
O(n*m*(2^m+O(spfa)))
//BZOJ 1294
#include <bits/stdc++.h>
using namespace std;
int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
const int maxn = 100010;
const int inf = 0x3f3f3f3f;
int n, m, D;
struct node1{
int x, y;
node1(){}
node1(int x, int y) : x(x), y(y) {}
}b[maxn];
struct node2{
int i, j, s;
node2(){}
node2(int i, int j, int s) : i(i), j(j), s(s) {}
};
int segcross(int x, int y, int nx, int ny, int S)
{
for(int i=1; i<=D; i++){
if(((x<b[i].x && nx>=b[i].x) || (x>=b[i].x && nx<b[i].x)) && y > b[i].y){
S ^= 1<<(i-1);
}
}
return S;
}
char mp[20][20];
int w[maxn], ans, dp[12][12][1010];
bool inq[12][12][1010];
void spfa(int sx, int sy){
queue <node2> que;
que.push(node2{sx, sy, 0});
memset(dp, 0x3f, sizeof(dp));
dp[sx][sy][0]=0;
inq[sx][sy][0]=1;
while(!que.empty()){
node2 u = que.front(); que.pop();
inq[u.i][u.j][u.s] = 0;
for(int k=0; k<4; k++){
int tx = u.i + dir[k][0], ty = u.j + dir[k][1];
if(tx > 0 && tx <= n && ty > 0 && ty <= m){
if(mp[tx][ty] != '0') continue;
int s = segcross(u.i, u.j, tx, ty, u.s);
if(dp[tx][ty][s] > dp[u.i][u.j][u.s]+1){
dp[tx][ty][s] = dp[u.i][u.j][u.s]+1;
if(!inq[tx][ty][s]){
inq[tx][ty][s]=1;
que.push(node2{tx,ty,s});
}
}
}
}
}
int mask = 1<<D;
for(int i=0; i<mask; i++){
int res = -dp[sx][sy][i];
for(int j = 1; j <= D; j++){
if(i&(1<<(j-1))) res += w[j];
}
ans = max(ans, res);
}
}
int main()
{
scanf("%d%d%d", &n, &m, &D);
for(int i=1; i<=D; i++) scanf("%d", &w[i]);
for(int i=1; i<=n; i++){
scanf("%s", mp[i]+1);
for(int j=1; j<=m; j++){
if(mp[i][j]>'0'&&mp[i][j]<='9'){
int x = mp[i][j]-'0';
b[x] = node1(i, j);
}
}
}
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if(mp[i][j]=='0'){
spfa(i, j);
}
}
}
printf("%d\n", ans);
return 0;
}