【题解】P2566 [SCOI2009] 围豆豆

1:性质一

根据射线定理,发现一个豆豆被围住当且仅当向右引一条射线与轮廓交点为奇数

2:性质二

要求出合法回路的价值,我们需要统计这些合法回路 包含的物品 以及 路径长

3:性质三

一些 从相同起点到\((x,y)\)包含的物品集合相同 的路径集合,它们是等价的

在这些等价路径中,我们只需要求它们的最小长度

4:操作

定义 \(val(S):S\) 集合价值和

枚举起点,跑 \(SPFA\) ,队列节点记录{位置,包含物品集合}

然后在 \(val(k)-dp[fx][fy][k]\) 中取最大值( \(fx,fy\) 为起点)

5:代码

#include<bits/stdc++.h>
#define Pair pair<int,int>
#define x first
#define y second
using namespace std;
const int N=11;
int nw[N];
int mp[N][N];
Pair line[N];

int vis[N][N][1<<N];
int dp[N][N][1<<N];
struct node{
	int x,y,S;
};
int n,m;int d;
int fx[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int check(int x,int y,int dx,int dy,int S){
	if(x==dx) return S;
	for(int i=0;i<d;i++){
		int mx=line[i].x,my=line[i].y;
		if(((mx==x&&mx<dx)||(mx==dx&&mx<x))&&my<y) S^=1<<i;
	}
	return S;
}

int val[1<<N];
int solve(int ftx,int fty){
	memset(dp,0x3f,sizeof dp);
	queue<node> q;
	q.push({ftx,fty,0});
	vis[ftx][fty][0]=1;
	dp[ftx][fty][0]=0;
	while(q.size()){
		node tmp=q.front();q.pop();
		int x=tmp.x,y=tmp.y,S=tmp.S;
		vis[x][y][S]=0;
		for(int dur=0;dur<4;dur++){
			int dx=x+fx[dur][0],dy=y+fx[dur][1];
			if(!(1<=dx&&dx<=n&&1<=dy&&dy<=m)||mp[dx][dy]) continue;
			int T=check(x,y,dx,dy,S);
			if(dp[x][y][S]+1<dp[dx][dy][T]){
				dp[dx][dy][T]=dp[x][y][S]+1;
				if(!vis[dx][dy][T]) q.push({dx,dy,T});
			}
		}
	}
	int ans=0;
	//if(ftx==3&&fty==2) cout<<dp[ftx][fty][3]<<"\n";
	for(int S=0;S<(1<<d);S++){
		ans=max(ans,val[S]-dp[ftx][fty][S]);
	}
	return ans;
}
int main(){
	cin>>n>>m;
	cin>>d;
	for(int i=0;i<d;i++) cin>>nw[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			char k;cin>>k;
			if(k=='0') mp[i][j]=0;
			else if(k=='#') mp[i][j]=-1;
			else{
				mp[i][j]=k-'0';
				line[mp[i][j]-1]={i,j};
			}
		}
	}
	for(int S=0;S<(1<<d);S++){
		for(int i=0;i<d;i++){
			if(S>>i&1) val[S]+=nw[i];
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(mp[i][j]==0) ans=max(ans,solve(i,j));
		}
	}
	cout<<ans<<"\n";
	return 0;
}

6:反思

轮廓包含节点可以考虑射线定理

posted @ 2026-06-25 20:37  Aistyr  阅读(4)  评论(0)    收藏  举报