hdu 5838 Mountain(状压dp+容斥原理)

Mountain

Time Limit: 3000/1500 MS (Java/Others)
Memory Limit:65536/65536 K (Java/Others)
Problem Description

Zhu found a map which is a N∗M rectangular grid.Each cell has a height and there are no two cells which have the same height. But this map is too old to get the clear information,so Zhu only knows cells which are valleys.

A cell is called valley only if its height is less than the heights of all its adjacent cells.If two cells share a side or a corner they are called adjacent.And one cell will have eight adjacent cells at most.

Now give you N strings,and each string will contain M characters.Each character will be ‘.’ or uppercase ‘X’.The j-th character of the i-th string is ‘X’ if the j-th cell of the i-th row in the mountain map is a valley, and ‘.’ otherwise.Zhu wants you to calculate the number of distinct mountain maps that match these strings.

To make this problem easier,Zhu defines that the heights are integers between 1 and N∗M.Please output the result modulo 772002.

Input

The input consists of multiple test cases.

Each test case begins with a line containing two non-negative integers N and M. Then N lines follow, each contains a string which contains M characters. (1≤N≤5,1≤M≤5).

Output

For each test case, output a single line “Case #x: y”, where x is the case number, starting from 1. And y is the answer after module 772002.

Sample Input

2 4
.X…
…X
4 2
X.


X.
1 2
XX

Sample Output

Case #1: 2100
Case #2: 2520
Case #3: 0

题意

有一个n*m的地图,每个点都有一个1~n*m间高度值,且每个点的高度值不同,如果一个点的高度值小于周围所有点,则称其为谷底,地图上将该点标记为’X’,否则则将地图上该点标记为’.’。求满足地图的不同的高度赋值方案数。

题解:

首先考虑对于原地图,可以发现谷底的数量其实是有限的(最多9个),而且谷地之间是不能相邻的。所以需要先检查原图是否合法。对于合法的图,考虑从小到大往图里填高度。对于一个高度,可以考虑填在谷地,叶可以考虑填在空地,但有限制,设数组 d p [ i ] [ j ] dp[i][j] dp[i][j],i表示将要填入的数字,j代表每个谷底是否已经赋值的状态(状压dp),设cnt=谷地数量。对于i+1这个高度,可以考虑赋给未赋过值的谷底,此时 d p [ i + 1 ] [ j ∣ ( 1 < < k ) ] + = d p [ i ] [ j ] dp[i+1][j|(1<<k)]+=dp[i][j] dp[i+1][j(1<<k)]+=dp[i][j]
也可以考虑赋给非谷地,总共有n*m-cnt种选择,但这之中有一些点是不合适的,比如已经赋过值的点,和未赋过值的谷地的周边(如果给分配在未赋值的谷地周边),那之后谷地无论如何高度都比它高,那么那个谷地就不是谷地了。综上考虑,赋过非谷地 d p [ i + 1 ] [ j ] + = d p [ i ] [ j ] ∗ ( p o s − i + m o u ) dp[i+1][j]+= dp[i][j]*(pos-i+mou) dp[i+1][j]+=dp[i][j](posi+mou)
其中posj 状态下其能放的位置,mou表示 j 状态下已放过的山谷的数量,

但如果仅是这样考虑显然会多统计一些情况,就是没有保证非谷地一定是非谷地,所以需要将这些减去,考虑容斥原理,将这些情况减去。具体实现,可以dfs,将一些非谷地在合法的前提下变为谷地,然后减去或加上这样的情况数量即可。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-8
 
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 100100;
const int mod = 772002;
char str[10][10], st[10][10];
int n, m, dx[8]={-1,-1,-1,0,0,1,1,1}, dy[8]={1,0,-1,1,-1,-1,0,1};
int pos[1020], x[10], y[10], mou[1020];
LL ans, dp[30][1020];
bool check();
LL solve(int num);
void dfs(int step, int num);

int main()
{
	int z=1, i, j;
	while(~scanf("%d %d", &n, &m))
	{
		for(i=0;i<n;i++)
			scanf(" %s", str[i]);
		if(!check())
			printf("Case #%d: 0\n", z);
		else{
			ans = 0LL;
			dfs(0, 0);
			printf("Case #%d: %lld\n", z, ans);
		}
		z++;
	}
	return 0;
}

bool check()
{
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(str[i][j] == 'X')
			for(int k=0;k<8;k++){
				int nx = i+dx[k], ny = j+dy[k];
				if(nx>=0 && nx<n && ny>=0 && ny<m && str[nx][ny] == 'X')
					return false;
			}
	return true;
}

LL solve(int num)
{
	int i, j, k, ks=n*m, kn, cnt = 0;
	for(i=0;i<n;i++)
		for(j=0;j<m;j++)
			if(str[i][j] == 'X'){
				x[cnt] = i, y[cnt] = j;
				cnt++;
			}
	kn = 1<<cnt;
	memset(st, '.', sizeof(st));
	for(i=0;i<kn;i++){
		mou[i] = 0;
		//统计j状态已放过的谷底的数目
		for(j=0;j<cnt;j++)
			if( !(i&(1<<j)))st[x[j]][y[j]] = 'Y';
			else mou[i]++;
		//求'.'能放的位置,即不在空白的谷底的周围的空地的数量
		pos[i] = ks-cnt;
		for(int a1=0;a1<n;a1++)
			for(int a2=0;a2<m;a2++){
				//如果一个非空地周围是一个未放过的谷地,则不能放在这个位置
				if(str[a1][a2]=='X')continue;
				for(k=0;k<8;k++){
					int nx = a1+dx[k], ny=a2+dy[k];
					if(nx>=0 &&nx<n &&ny>=0 &&ny<m && st[nx][ny] == 'Y'){
						pos[i]--;break;
					}
				}
			}
		for(j=0;j<cnt;j++)
			if( !(i&(1<<j)))st[x[j]][y[j]] = '.';
	}
	for(i=0;i<=ks;i++)
		for(j=0;j<=kn;j++)
			dp[i][j] = 0;
	dp[0][0] = 1;
	for(i=0;i<ks;i++)
		for(j=0;j<kn;j++){
			if(!dp[i][j])continue;
			for(k=0;k<cnt;k++)
				if(!(j&(1<<k)))
					dp[i+1][j|(1<<k)] = (dp[i+1][j|(1<<k)]+dp[i][j])%mod;
			//pos[j]-i+mou[j]表示可以放的位置数量,减去已经放过的数量
			//再加上放在谷底的数的数量
			dp[i+1][j] = (dp[i+1][j] + dp[i][j]*(pos[j]-i+mou[j]) % mod)%mod;
		}
	return dp[ks][kn-1];
}
//容斥求状态数
void dfs(int step, int num)
{
	if(step == n*m){
		if(num%2==0)ans = (ans + solve(num))%mod;
		else ans = (ans-solve(num)+mod)%mod;
		return ;
	}
	int i, x = step/m, y = step%m;
	for(i=0;i<8;i++)
	{
		int nx = x+dx[i], ny = y+dy[i];
		if(nx>=0 && nx<n && ny>=0 && ny<m && str[nx][ny] == 'X')break;
	}
	//如果当前非谷地可能会被赋值为谷底的情况,则将其作为谷地,并加或减去状态数
	if(i==8 && str[x][y] != 'X'){
		str[x][y] = 'X';
		dfs(step+1, num+1);
		str[x][y] = '.';
	}
	dfs(step+1, num);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值