BZOJ1087=Codevs2451=洛谷P1896&P2326互不侵犯

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2885  Solved: 1693
[Submit][Status][Discuss]

Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案数。

Sample Input

3 2

Sample Output

16

HINT

Source

一开始的时候,使劲往规律方面靠,20分钟过去了,没发现什么通用规律

于是就暴力写搜索(建议用bfs写,略快),写完一看,数据这么小,记忆化?还是直接list吧。

打了一个小时的表。终于功夫不负有心人啊,AC了。

AC代码<打表代码>(民间版)

#include<bits/stdc++.h>
using namespace std;//规律:k>((n+1)/2)^2 必为0
long long ys[10][82]={{0LL},
                {1LL,1LL},
                {1LL,4LL},
                {1LL,9LL,16LL,8LL,1LL},
                {1LL,16LL,78LL,140LL,79LL},
                {1LL,25LL,228LL,964LL,1987LL,1974LL,978LL,242LL,27LL,1LL},
                {1LL,36LL,520LL,3920LL,16834LL,42368LL,62266LL,51504LL,21792LL,3600LL},
                {1LL,49LL,1020LL,11860LL,85275LL,397014LL,1220298LL,2484382LL,3324193LL,2882737LL,1601292LL,569818LL,129657LL,18389LL,1520LL,64LL,1LL},
                {1LL,64LL,1806LL,29708LL,317471LL,2326320LL,12033330LL,44601420LL,119138166LL,229095676LL,314949564LL,305560392LL,204883338LL,91802548LL,25952226LL,4142000LL,281571LL},
                {1LL,81LL,2968LL,65240LL,962089LL,10087628LL,77784658LL,450193818LL,1979541332LL,6655170642LL,12848094442LL,29492596820LL,46439242830LL,57647295377LL,49138545860LL,31122500764LL,14518795348LL,4959383037LL,1237072414LL,224463798LL,29275410LL,2673322LL,163088LL,6150LL,125LL,1}};
int main(){
	int n,k;
	cin>>n>>k;
	cout<<ys[n][k];
	return 0;
}

下面我为大家手抄了一遍网上的正解(所谓的状压dp)

AC代码+题解(官方版):

//dfs预处理+状压dp:将2^9-1种状态压缩为m种可行状态,循环次数大大减少
#include<cstdio>
using namespace std;
#define N 100
long long int f[N][N][600],ans;//f[i][j][k]=放棋子到第i行,且已经放了j个棋子,此时第i行状态为k的方案数,ans=最终方案数
int stay[N];//stay[i]=第i种可行状态
int map[N][N],cnt[N];//cnt[i]=第i种状态对应棋子数
int n,k,m=0;
void pre_dfs(int x,int pos,int now){//dfs预处理枚举出同一行内互不冲突的状态,x是已经放了的棋子数,n是当前放棋子的位置,now=当前行状态
	int i;
	stay[++m]=now;//新增一种可行状态
	cnt[m]=x;
	if(x>=(n+1)/2||x>=k) return;//如果已经放的棋子数超过格子半数,明显不能再放了,退出
	for(i=pos+2;i<=n;i++) pre_dfs(x+1,i,now+(1<<(i-1))); //枚举下一颗棋子放的位置
}
void pre_map(){
	int i,j;
	for(i=1;i<=m;i++){//第一行状态
		for(j=1;j<=m;j++){//第二行状态
			map[i][j]=map[j][i]=((stay[i]&stay[j])||((stay[i]>>1)&stay[j])||((stay[i]<<1)&stay[j]))?0:1; //当第一行某个点和第二行某个点在对角线或同一列时,两行冲突了
		}
	}
	for(i=1;i<=m;i++) f[1][cnt[i]][i]=1;//dp预处理
}
int main(){
	scanf("%d%d",&n,&k);
	pre_dfs(0,-1,0); //预处理枚举出同一行所有可行方案,减少DP循环次数
	pre_map(); //预处理上下左右冲突的情况以及dp初始化
	for(int i=2;i<=n;i++){//i行
		for(int j=0;j<=k;j++){//j个棋子
			for(int now=1;now<=m;now++){
				if(cnt[now]>j) continue; //当前已放的棋子数比这一行状态对应棋子数少,显然不符合题意,跳过
				for(int h=1;h<=m;h++) //枚举上一行状态
					if(map[h][now]&&cnt[h]+cnt[now]<=j) f[i][j][now]+=f[i-1][j-cnt[now]][h]; //符合条件,加上上一行的可行方案数
			}
		}
	}
	for(int i=1;i<=m;i++)ans+=f[n][k][i]; //统计答案
	printf("%lld\n",ans);
	return 0;
}

  

时间: 2024-10-22 15:17:03

BZOJ1087=Codevs2451=洛谷P1896&P2326互不侵犯的相关文章

洛谷 P1896 [SCOI2005]互不侵犯King

题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入输出格式 输入格式: 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) 输出格式: 所得的方案数 输入输出样例 输入样例#1: 3 2 输出样例#1: 16 #include<cstdio> #include<cstring> #include<ios

洛谷 P1896 互不侵犯King

P1896 [SCOI2005]互不侵犯King 题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入输出格式 输入格式: 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) 输出格式: 所得的方案数 输入输出样例 输入样例#1: 3 2 输出样例#1: 16

【洛谷P1896【SCOI2005】】互不侵犯King

题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入输出格式 输入格式: 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) 输出格式: 所得的方案数 输入输出样例 输入样例#1: 3 2 输出样例#1: 16 算法: 状压DP   分析: 这道题乍眼一看以为是搜索,其实不然,这道题实则是一道动规的题目. 虽然这道题目看上去和corn

P1896 [SCOI2005]互不侵犯King

题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入输出格式 输入格式: 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) 输出格式: 所得的方案数 输入输出样例 输入样例#1: 3 2 输出样例#1: 16 题解:状态压缩动规,用一个整型的二进制表示来表示棋盘上一行的情况,放了棋子为1,没放为0:可以先预处理出所有可能的状态:用位

#(状压DP)P1896 [SCOI2005]互不侵犯(提高+/省选-)

题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 注:数据有加强(2018/4/25) 输入格式 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) 输出格式 所得的方案数 输入输出样例 输入 #1复制 3 2 输出 #1复制 16 #include<cstdio>#include<iostream>using

P1896 [SCOI2005]互不侵犯

______________________________________________________________ 和上一题差不多,注意初始化与判断即可 #include<bits/stdc++.h> using namespace std; long long int n,kk,tot,can[4096],dp[11][1096][121],cnt[4096]; long long ans; int main() { cin>>n>>kk; for(int

Luogu P1896 [SCOI2005]互不侵犯

gate 第二道状压dp... 预处理每种状态j所含1的个数为sum[j] f[i][j][l]代表第i行,状态为j,当前共有l个国王 枚举本层状态j,上一层状态k,判断八方向是否有相邻:k&j||(k<<1)&j||(k>>1)&j 枚举国王数l,则有f[i][j][l] += f[i-1][k][l-sum[j]] 最后统计f[n][ ][m]即为答案 代码如下 #include<cstdio> #include<iostream>

[SCOI2005]互不侵犯

题目:BZOJ1087.洛谷P1896.codevs2451. 题目大意:在n×n的棋盘上放k个王,要使它们互相攻击不到,有几种放法? 一个王能攻击到与它相邻的八格内的棋子. 解题思路:状压DP. 我们可以用一个二进制来表示当前行的状态(1表示放了王,0表示没有). 则设f[i][j][p]表示前i行放j个王状态为p时的方案数,则有: $f[i][j][p]=\sum\limits_{s=0}^{2^n-1}f[i-1][j-len][s]$.其中len表示p状态中有多少个1. 当然必须保证状态

1896 [SCOI2005]互不侵犯 状压dp

传送门 这是一道状压dp的经典例题 题目让输出所有可能的方案数 很显然 这是一道动态规划了 由于国王放置的位置有一定的限制 所以我们要在状态转移的过程中增加一维来存储状态 我们这一道题假设f[i][j][k] 意思是在前i行一共放置了j个国王 第i行国王放置的状态是k  存储的值是方案数 首先 我们可以先预处理出左右合法的每行的状态...(有点绕 就是说针对单独的一行 会有那些合法的状态 使得相邻的两个格子最多只能放置一个国王 没有相邻的国王 然后我们开始dp 我们分别枚举i k s i是当前枚