P3160 [CQOI2012]局部极小值
[CQOI2012]局部极小值
题目描述
有一个
如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。
答案对
- 对于
的数据,保证 , 。
思路点拨
如果我们只让题目给的局部最小值点满足条件,其余的点随意放,可能导致产生新的局部最小值。所以这不好搞,我们就先简化问题,不考虑其余新的局部最小值情况。
如何求出一个给定地图中,满足条件的方案数呢?我们考虑状态压缩,数据范围十分明显了。但是我们有仔细一想,
这样我们只需要状压那些局部最小值点就可以了。整合一下,我们定义
转移分两类,一种是选了一个局部最小值点:
另一种就是选择了非局部最小值点。假设目前状态
OK,这样就解决了我们的第一部分,动态规划。这里贴出部分代码,变量名是一样的。
bool vis[MAXN][MAXN];
int dp(){
int cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]=='X')
add(i,j,cnt);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int t=0;t<(1<<cnt);t++){
memset(vis,0,sizeof(vis));
for(int i=0;i<cnt;i++)
if(!(t&(1<<i))){
vis[p[i+1].x][p[i+1].y]=1;
for(int j=0;j<8;j++)
vis[p[i+1].x+X[j]][p[i+1].y+Y[j]]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
g[t]+=vis[i][j];
g[t]=n*m-g[t];
}
f[0][0]=1;
for(int i=1;i<=n*m;i++){
for(int t=0;t<(1<<cnt);t++){
if(g[t]-i+1>0) f[i][t]=(f[i][t]+f[i-1][t]*(g[t]-i+1))%mod;
for(int j=0;j<cnt;j++)
if(t&(1<<j))
f[i][t]=(f[i][t]+f[i-1][t-(1<<j)])%mod;
}
}
return pow(-1,cnt-tot)*f[n*m][(1<<cnt)-1]+mod;
}
但是这样是不对的,应为我们在除了局部最小值点之外的点乱填的时候,可能导致新的局部最小值点产生。所以我们直接容斥。
画画韦恩图可知这既是一个一般容斥(
具体实现爆搜就可以了,方案不会很多。
最终给出前部代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-f;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int MAXN=9,mod=12345678;
int n,m;
char a[MAXN][MAXN];
int f[30][1<<MAXN],g[1<<MAXN];
int X[8]={1,-1,0,0,1,1,-1,-1};
int Y[8]={0,0,1,-1,1,-1,1,-1};
struct node{
int x,y;
}p[MAXN];
void add(int x,int y,int &tot){
++tot;
p[tot].x=x;
p[tot].y=y;
}
int check(int x,int y){
int cnt=0;
for(int i=0;i<8;i++){
int xx=x+X[i],yy=y+Y[i];
if(a[xx][yy]=='X') cnt++;
}
return cnt;
}
bool Error(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]=='X'&&check(i,j))
return 0;
return 1;
}
int sum[MAXN],tot;
void prepare(){
sum[0]=1;
for(int i=1;i<MAXN;i++)
sum[i]=sum[i-1]*i;
}
int C(int n,int m){
return sum[n]/(sum[m]*sum[n-m]);
}
bool vis[MAXN][MAXN];
int dp(){
int cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]=='X')
add(i,j,cnt);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int t=0;t<(1<<cnt);t++){
memset(vis,0,sizeof(vis));
for(int i=0;i<cnt;i++)
if(!(t&(1<<i))){
vis[p[i+1].x][p[i+1].y]=1;
for(int j=0;j<8;j++)
vis[p[i+1].x+X[j]][p[i+1].y+Y[j]]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
g[t]+=vis[i][j];
g[t]=n*m-g[t];
}
f[0][0]=1;
for(int i=1;i<=n*m;i++){
for(int t=0;t<(1<<cnt);t++){
if(g[t]-i+1>0) f[i][t]=(f[i][t]+f[i-1][t]*(g[t]-i+1))%mod;
for(int j=0;j<cnt;j++)
if(t&(1<<j))
f[i][t]=(f[i][t]+f[i-1][t-(1<<j)])%mod;
}
}
return pow(-1,cnt-tot)*f[n*m][(1<<cnt)-1]+mod;
}
int ans;
void dfs(int x,int y){
if(y==m+1){
if(x==n) ans=(ans+dp()+mod)%mod;
else dfs(x+1,1);
}
else{
if(a[x][y]=='X') dfs(x,y+1);
else{
dfs(x,y+1);
if(!check(x,y)){
a[x][y]='X';
dfs(x,y+1);
a[x][y]='.';
}
}
}
}
signed main(){
cin>>n>>m;
prepare();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]=='X') tot++;
if(!Error()) cout<<0;
else{
dfs(1,1);
cout<<ans;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现