poj1321 棋盘问题(状态压缩)

来源:互联网 发布:淘宝宝贝描述用什么做 编辑:程序博客网 时间:2024/05/21 01:30
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 对于每一组数据,给出一行输出,输出摆放的方案数目#..#4 4...#..#..#..#...-1 -1

Sample Output

21

Source

蔡错@pku

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


/*非常棒的解法:状态压缩DP!!关键点:1.(j&(1<<(u-1)))==0)的判断决定了dp[i][j]是否=0  即有.(不可放旗位子)时,它决定了在#可以放,即dp[i][j]不为02.这里的二进制是倒过来的ex:一般是3=011;这里3=110  因为要与图配合+方便表示(当然你也可以把它弄成一般的(1<<(u-1))改成(1<<(n-u))即可//*/#include<iostream>#include<cstdio>#include<cstring>using namespace std;#define NN 300int 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 0
原创粉丝点击