poj1321 棋盘问题(状态压缩)

14 篇文章 0 订阅
7 篇文章 0 订阅
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 
对于每一组数据,给出一行输出,输出摆放的方案数目
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1

Source

[Submit]   [Go Back]   [Status]   [Discuss]


 
/*
非常棒的解法:状态压缩DP!!
关键点:
1.(j&(1<<(u-1)))==0)的判断决定了dp[i][j]是否=0
  即有.(不可放旗位子)时,它决定了在#可以放,即dp[i][j]不为0
2.这里的二进制是倒过来的ex:一般是3=011;这里3=110
  因为要与图配合+方便表示(当然你也可以把它弄成一般的(1<<(u-1))改成(1<<(n-u))即可
//*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define NN 300
int ill[12][NN],num[NN];
int dp[12][NN];
//dp[i][j]:1到i行已覆盖j列的方案数
int i,j,l,n,k,status,newst,ans,u;
char g[12][12];
void cal(int now){
    int i,j,tmp,flag,cnt;
    tmp=1;
    cnt=0;
    for(i=1;i<=n;++i){
        if (now&tmp) cnt+=1;
        tmp=tmp<<1;
    }
    num[now]=cnt;   
}
void pbinary(int tj)
{
	int flag=0,bn=tj;
	while(tj)
	{
		if(tj&1)cout<<1;
		else cout<<0;
		tj/=2;
		flag++;
	}
	while(flag<n)
	{
		cout<<0; flag++;
	}cout<<"  ---> "<<bn<<endl;
}
int main()
{
	//freopen("in.h","r",stdin);
    while(1)
	{
        scanf("%d%d",&n,&k);
        if (n==-1&&k==-1) break;
        for(i=1;i<=n;++i) scanf("%s",g[i]+1);
        status=1<<n;
        for(i=0;i<status;++i) cal(i);
//num[i]存的是i对应2进制的1的位数(即表已经放了num[i]个旗子在棋盘)
//代表的是每一行可以放的状态.如n=2有4种状态(用0~3的二进制表示)
        //for(i=0;i<status;++i) cout<<num[i]<<" ";cout<<endl;
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(i=1;i<=n;++i)
		{
            for(j=0;j<status;++j)
			if (num[j]<=k)//(棋盘已放num[j]个旗子<=要放)
			{
                dp[i][j]+=dp[i-1][j];	//+=:下面产生的或会影响f[i][j]
//当前行不加棋子(也相当于把上次状态转移到当前)
//若上一行的j状态还未出现,则有dp[i-1][j]=0
                for(u=1;u<=n;++u)
				if ((g[i][u]=='#')&&(j&(1<<(n-u)))==0)
				{
//(1<<(u-1))表把第u位转为二进制,比如:u=3;(1<<(u-1))=4即二进制100倒数第3个(即第一个)为1
//目的是和状态j(1表已放位置)的二进制比较,看是否不在同列
                    //newst=j|(1<<(u-1));
					newst=j|(1<<(n-u));
					dp[i][newst]+=dp[i-1][j];

					/*pbinary(j);
					pbinary(1<<(u-1));
					pbinary(newst);//*/
//若上一行的j状态还未出现,则有dp[i-1][j]=0
//当前行加棋子,由j状态到newst状态,故f[i-1,j]+到f[i,newst]
                }
            }
        }
        for(ans=i=0;i<status;++i)
            if (num[i]==k) {ans+=dp[n][i];}
		//最后将最后一行棋子数等于k的状态的方案数求和即可。
        printf("%d\n",ans);
    }
    return 0;
}
//c框from:http://blog.csdn.net/asdfgh0308/article/details/8454022
</pre><pre name="code" class="cpp">网上满天下的dfs,这里就不多说了
<pre name="code" class="cpp">#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,ans,flag;
int vis[11][11],a[11][11],c[11];
char s[11][11];
void dfs(int x,int de)
{
	if(de==m)
	{
		flag++;
		return ;
	}
	int i,j;
	for(i=x+1;i<=n;i++)	//防止一行都不是旗位
	{
		for(j=1;j<=n;j++)
		{
			if(c[j] || a[i][j]==0)continue;
			c[j]=1;	  //放
			dfs(i,de+1);
			c[j]=0;	  //不放
		}
	}
}
int main()
{
	int i,j;
	while(~scanf("%d%d",&n,&m))
	{
		if(n==-1 && m==-1)break;
		memset(c,0,sizeof(c));
		memset(a,0,sizeof(a));
		flag=0;
		for(i=1;i<=n;i++)
		{
			scanf("%s",s[i]);
			for(j=0;j<strlen(s[i]);j++)
				if(s[i][j]=='#')a[i][j+1]=1;
		}
		dfs(0,0);
		printf("%d\n",flag);
	}
	return 0;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值