zoukankan      html  css  js  c++  java
  • Luogu P1896[SCOI2005]互不侵犯

    状态压缩典型例子?

    一定要说的一点:

    (color{#fccefa}{不开long long一时爽,提交代码火葬场})

    SOLUTION:

    定义数组(dp[i][j][s]),

    表示当前要放第i行的国王

    算上第i行放上的国王共有j个国王已经被放在棋盘上了

    第i行的状态是s

    那么可以考虑转移:

    (dp[i][j][s]=sum {dp[i-1][j-cnt(s)][s']|((s'<<1)&s)||((s'>>1)&s)||(s' &s)==0})

    其中,s'表示上一行的状态,要保证互不侵犯,则:

    国王周围的八个格子显然都不可以再放国王,那么显然从0~(1<<n)的状态并不是都可以取到的(就如(3_{10} o 011_{2})显然就是不符合要求的),那么我们就不能简单的从0~(1<<n)枚举子集(为什么要说这个呢,因为我就直接枚举了,然后就炸了√)

    需要进行dfs来把合法的子集处理出来,这里用了两个数组

    num[S] 记录状态为S的摆法中,有多少个1

    kind[Cnt] 记录一种符合要求的摆法,其中Cnt是符合要求的摆法的总个数

    具体代码如下:

    void dfs(int cnt,int now,int nu,int bj){
    	if(cnt==n+1) return;
    	if(num[now]==-1)
    		kind[++Cnt]=now;
    	num[now]=nu;
    	if(bj==0) dfs(cnt+1,now+(1<<cnt),nu+1,1);
    	dfs(cnt+1,now,nu,0);
    }
    

    dfs也就是处理长度为n的一行每两个1相隔至少为1个空位置(0)的摆法

    其中:

    cnt 表示当前枚举到了第几位

    now 表示当前状态下,二进制所对应的十进制是多少

    nu 表示目前一共填了几个1

    bj 用于判断上一位填的是1还是0

    其中0表示上一位填的0 1表示上一位填的1

    显然如果上一位填的是1,那么这一位我们只能填0,因此当bj==1时,我们只dfs这一位填0的情况,否则dfs两种情况。因为会重复搜索到(鬼知道为什么,所以要判断之前num[now]是否!=-1了(之前是判断!=0来着,后来发现num[0]就等于0),如果=-1,就需要记录下来,!=-1,就不需要再记录一遍了.

    初始状态是(dp[1][num[kind[i]]][kind[i]]=1)

    也就是第一行的国王,每一种状态都有一种方法√;

    然后就可以愉快的AC

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,k,Cnt;
    long long dp[10][100][1000];
    int num[1000],kind[1000];
    
    void dfs(int cnt,int now,int nu,int bj){
    	if(cnt==n+1) return;
    	if(num[now]==-1)
    		kind[++Cnt]=now;
    	num[now]=nu;
    	if(bj==0) dfs(cnt+1,now+(1<<cnt),nu+1,1);
    	dfs(cnt+1,now,nu,0);
    }
    bool cmp(int a,int b) {
    	return a<b;
    }
    int main(){
    	scanf("%d%d",&n,&k);
    	memset(num,-1,sizeof(num));
    	dfs(0,0,0,0);
    	sort(kind+1,kind+Cnt+1,cmp);
    	for(int i=1;i<=Cnt;i++) dp[1][num[kind[i]]][kind[i]]=1;
    	for(int i=2;i<=n;i++) {
    		for(int a=1;a<=Cnt;a++) {
    			for(int b=1;b<=Cnt;b++) {
    				if(kind[a]&kind[b]) continue;
    				if(kind[a]&(kind[b]<<1)) continue;
    				if(kind[a]&(kind[b]>>1)) continue;
    				for(int j=k;j>=num[kind[a]];j--) 
    					dp[i][j][kind[a]]+=dp[i-1][j-num[kind[a]]][kind[b]];
    			}
    		}
    	}
    	long long ans=0;
    	for(int i=1;i<=Cnt;i++) ans+=dp[n][k][kind[i]];
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    iOS核心动画高级技巧之核心动画(三)
    iOS核心动画高级技巧之图层变换和专用图层(二)
    iOS核心动画高级技巧之CALayer(一)
    Swift高级语法学习总结
    Swift基础语法学习总结
    Swift语法总结补充(一)
    【思图佳预约(一)】在做预约挂号小程序时,text标签出现一截多余的空白?
    【随笔】清除浮动float的几种办法
    ----原生js实现飞机大战小游戏<第二篇>----@半城言沙
    ----原生js实现飞机大战小游戏<第一篇>----@半城言沙
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11797092.html
Copyright © 2011-2022 走看看