HDu 4529(状態dp)

3874 ワード

鄭工場長シリーズ物語——N騎士問題
Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Problem Description
鄭工場長は正工場長ではない
副工場長でもない
彼は工場長ではない
それともあのテンセント会社の私たちですか?
余暇に碁を打つのが好きな私たち
  
最近、鄭工場長は八皇后問題に興味を持って、チェスを持って何日も研究して、やっと研究が終わった.興奮のあまり、碁盤の前に座っていた彼はまた退屈になった.何気なく、彼は目の前の碁盤の上でただ8人の皇后だけを並べて、がらんとしていることを感じて、ちょうどまた身の回りにいくつかの騎士がいることを発見して、そこで、彼はこれらの騎士も碁盤の上に並べたいと思って、もちろん碁盤の上の1つの位置は1つの駒しか置くことができません.八皇后問題の影響で、彼は自分がこれらの騎士を並べた後、2人の騎士の間で互いに攻撃できないことを満たすことを望んでいる.
今、鄭工場長は何種類の振り方があるか知りたいのですが、彼を助けてもらえますか.
騎士の下法:
各将棋はまず横に歩いたり、まっすぐ行ったりしてから、外に斜めに1格歩きます.あるいはまず斜めに1格を歩いて、最後に更に外へ横に歩いてあるいは縦に1格を歩いて(つまり“日”の字を歩きます).越子ができて、“中国将棋”の“下手な馬の足”の制限がありません.
 
Input
第1の動作の整数T(1<=T<=8)を入力し、T組のテストデータがあることを示す.
各グループのデータはまず整数N(1<=n<=10)であり、N人の騎士を置くことを示す.
次は8*8の行列で碁盤を記述します.'.'はこの位置が空であることを示し、'*'はこの位置に皇后が置かれていることを示します.
データの初期盤は合法的な八皇后振り法であることを保証している.
 
Output
各グループのデータに対して、合法的なシナリオ数を表す整数を1行に出力します.
 
Sample Input

   
   
   
   
2 1 *....... ....*... .......* .....*.. ..*..... ......*. .*...... ...*.... 2 *....... ....*... .......* .....*.. ..*..... ......*. .*...... ...*....

 
Sample Output

   
   
   
   
56 1409

 
Source
2013テンセントプログラミングマラソン初試合第5試合(3月25日) 
解題構想:定義状態dp[i][j][p][q]:前i行を表し、j個の騎士を使用し、i行目の状態はp、i-1行目の状態はqのシナリオ数である.これは私の最初の考えですが、後で時間が複雑になると思うと、ずっと書く勇気がなくて、結局他の人の解題報告書を見て、確かにそう思っていましたが、意外にもタイムアウトしていないことに驚きました.
AC:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int t,N;
char s[10][10];
bool suit[10][1<<8+5];
int dp[8][11][1<<8][1<<8];//dp[i][j][a][b], i , i j   ,i   a,i-1   b    
int one[1<<8+5];
//          
inline void init()
{
    memset(suit,0,sizeof(suit));
    for(int i=0;i<8;i++)
    {
        for(int j=0;j< 1<<8;j++)
        {
            int tag=1;
            for(int k=0;k<8;k++)
            {
                if(s[i][k]=='*'&&(j&(1<<k)))
                {
                    tag=0; break;
                }
            }
            if(tag) suit[i][j]=1;
        }
    }
}
inline int getOne(int i)
{
    int ans=0;
    while(i)
    {
        ans+=i%2;
        i/=2;
    }
    return ans;
}

int main()
{
    for(int i=0;i<(1<<8);i++)
        one[i]=getOne(i);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&N);
        for(int i=0;i<8;i++) scanf("%s",s[i]);
        init();
        memset(dp,0,sizeof(dp));
        for(int i=0;i<(1<<8);i++)
        {
            if(suit[0][i] && one[i]<=N)
            {
                dp[0][one[i]][i][0]=1;
            }
        }

        for(int i=1;i<8;i++)
           for(int n=0;n<=N;n++)
            for(int j=0;j<(1<<8);j++)
			{
				if(one[j] > n) continue;
				if(!suit[i][j]) continue;
				for(int k=0;k< 1<<8;k++)
				{
					if(k & (j<<2)) continue;
					if(k & (j>>2)) continue;
					for(int r = 0;r < (1<<8);r++)
					{
						if(r & (j<<1)) continue;
						if(r & (j>>1)) continue;
						dp[i][n][j][k]+=dp[i-1][n-one[j]][k][r];
					}
				}
			}
        int ans=0;
        for(int i=0;i<(1<<8);i++)
        {
            if(suit[7][i])
            {
                for(int j=0;j<(1<<8);j++)
                {
                    if(suit[6][j])
                        ans+=dp[7][N][i][j];
                }
            }
        }
        printf("%d
",ans); } }