题解 P2566 【[SCOI2009]围豆豆】
xukuan
2020-10-29 12:02:15
不明白为什么有dp的标签,这题个人更倾向于状压spfa
判断点是否在多边形内:从这个点向外引一条射线,若与多边形相交了奇数次,就在它的内部,否则在外部。
为了防止各种诡异情况(比如多边形类似“凹”字,然后过他的顶点;射线与边重合不算相交),这题的射线采用向正右方的射线
先来设计最短路的状态
用$f_{x,y,S}$表示当前在点$(x,y)$,状态为S要走的最短步数
$1 \leq D \leq 9$,显然状压,S的第i位表示数字i是否在多边形内
然后设计状态转移
如果当前状态为S,下一个点的状态为T,怎么从S推出T
1. 横着走:T=S
2. 竖着走:如果有一个数字在当前点的正左侧或者下一个点的正左侧(你不能确定下一步会不会转弯,因为这样就变成了重合,所以要算上当前点),那么改变它的状态(原来不在图形内的变成在内,在内的变成在外)
搜索题的特点是思路简单代码长,这题就说到这里,直接上代码
```cpp
#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const ll N=15,D=10;
const ll dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
ll n,m,k,ans,a[N],g[1<<D],f[N][N][1<<D],v[N][N][1<<D];
char s[N][N];
struct{
ll x,y;
}place[N];
struct node{
ll x,y,S;
};
queue<node> q;
inline bool check(ll x,ll y){
return x>=1&&x<=n&&y>=1&&y<=m&&s[x][y]=='0';
}
inline ll getT(ll x,ll y,ll X,ll Y,ll S){
ll T=S;
for(ll i=1; i<=k; i++){
if(((x==place[i].x&&X<place[i].x)||(x<place[i].x&&X==place[i].x))&&Y>place[i].y) T^=1<<(i-1);
}
return T;
}
inline void spfa(node S){
while(!q.empty()) q.pop();
memset(v,0,sizeof(v));
memset(f,0x3f,sizeof(f));
q.push(S); v[S.x][S.y][S.S]=1;
f[S.x][S.y][S.S]=0;
while(!q.empty()){
ll x=q.front().x,y=q.front().y,S=q.front().S; q.pop();
v[x][y][S]=0;
for(ll i=0; i<4; i++){
ll X=x+dx[i],Y=y+dy[i];
if(check(X,Y)){
ll T=S;
if(i==0||i==2) T=getT(x,y,X,Y,S);
if(f[x][y][S]+1<f[X][Y][T]){
f[X][Y][T]=f[x][y][S]+1;
if(!v[X][Y][T]){
q.push(node{X,Y,T});
v[X][Y][T]=1;
}
}
}
}
}
for(ll i=0; i<(1<<k); i++) ans=max(ans,g[i]-f[S.x][S.y][i]);
}
int main(){
cin>>n>>m>>k;
for(ll i=1; i<=k; i++) scanf("%lld",&a[i]);
for(ll i=1; i<=n; i++){
scanf("%s",s[i]+1);
for(ll j=1; j<=m; j++){
if(s[i][j]>='1'&&s[i][j]<='9'){
ll now=s[i][j]-48;
place[now].x=i;
place[now].y=j;
}
}
}
for(ll i=0; i<(1<<k); i++){
for(ll j=1; j<=k; j++){
if(i&(1<<(j-1))) g[i]+=a[j];
}
}
for(ll i=1; i<=n; i++){
for(ll j=1; j<=m; j++){
if(s[i][j]=='0') spfa(node{i,j,0});
}
}
cout<<ans<<endl;
return 0;
}
```