羅バレー-P1896 [SCOI2005](圧力DP || DFSのような)非侵略

タイトル説明

王は、彼らがそこに配置されているどのように多くのプログラム、お互いを攻撃しないことを、N×Nのチェス盤にKを置きます。王は左にそれを攻撃し、右、左上の8つのグリッドの総数の近傍における各グリッドの右上の右下の8つの方向を左に低下させることができます。

注:データは強化しています(2018年4月25日)

入力形式

二つの数字N、K(1 <= N <= 9、0 <= K <= N * N)を含む唯一のライン、

出力フォーマット

結果の数値プログラム

入力#1

3 2

出力#1

16

問題解決のアイデア:

DPソリューション:

N <言い換える10は、バイナリビットは、次いで、最も1024における状態数を点の状態を示すため、最初、各層内部の法的地位を考えます。彼は連続バイナリビットではありません。1.隣接チェス、攻撃することができますので、
これは状態のすべての法的数を記録するために、直接前処理することができます。状態の数と1の対応する番号。唯一のK個の対象からです。
その後、層の間の関係を検討してください。層によって上下層から検討します。そして、前の層の新たな層は、彼だけに影響を与えることができます。被験者によれば、2つの状態の動作で行うことが0に等しく、かつ、左と右のいずれかまたは0に等しく、法的缶共存あります。その後、状態の層数に新しいレイヤーを追加します。
最後に、トピックがk個を取るように求めているので、中に入れてきた作品の合計数を考慮すべき場所があります。次いで、各DPの値は、個の対応する電流の合計数で記録されるべきです。層を追加する場合、それはまた、STA [ID]ポーンを増加させます。
一見高い3つのサイクルのコードの複雑さは、実際には、とき前の法的地位は、より状態の半分以上を除去するであろう、その複雑さは完全に許容です。

DFSソリューション:

DP溶液と最初のステップは、最初の場合和の場合、すべての法的地位、および数1を前処理し、次いで第一層、シーケンス内の追加層、条件は、最後に列挙第n層同一で列挙K、1が返されます。
DFSのコードはより簡潔が、注意が必要それ以外の場合は、TLEで重複する検索結果の多くになり、メモリの必要性です。場合のメモリ、三つのパラメータが必要とされ、層の数、状態数、及びメモリの断片の合計。簡単な剪定と相まって、あなたACことができます。

ACコード(DP):

#include <bits/stdc++.h>
#define int long long
using namespace std;
int dp[10][100][1024];	// 三维分表代表当前层数,当前放棋子数,当前状态数
int legal[1024];		// 记录合法状态
int id[1024];			// 记录状态数对应的1的个数

signed main()
{
    memset(legal,0,sizeof(legal));
    memset(id,0,sizeof(id));
    int n,m;
    cin>>n>>m;
    for(int i = 0 ; i < 1024 ; i ++)	// 预处理出一层所有的合法状态
    {
       if(((i<<1)&i) == 0 && ((i>>1)&i) == 0)
          legal[i] = 1;
       int cnt =0;
       for(int j = i ; j != 0 ; j >>= 1)
           if(j&1) 		cnt ++;
       if(cnt > m) 		legal[i] = 0;
       else 			id[i] = cnt;
    }
    for(int i = 0 ; i < (1<<n) ; i ++)
        if(legal[i])
            dp[1][id[i]][i] = 1;
    for(int i = 2 ; i <= n ;i ++)	// 枚举层数
        for(int j = 0 ; j < (1<<n) ; j ++)	// 枚举本层状态
            if(legal[j])
                for(int sta = 0; sta < (1<<n) ;sta ++)	// 枚举上一层状态
                    if(legal[sta] && (j&sta) == 0  && ((j<<1)&sta) == 0 && ((j>>1)&sta) == 0 )
                        for(int k = 0 ; k <= m-id[j] ; k ++)
                            dp[i][k+id[j]][j] += dp[i-1][k][sta];
    int ans = 0;
    for(int j = 0 ; j < (1<<n); j ++) // 计算最后方案数总和
        ans += dp[n][m][j];
    cout<<ans<<endl;
}


ACコード(DP):

#include <bits/stdc++.h>
#define int long long
using namespace std;
int dp[10][100][1024];		// 记忆化数组
int legal[1024];			// 合法状态
int id[1024];
int n,m;

int dfs(int level,int sta,int sum) 
{
    if(sum > m || level > n) return 0;
    if(level == n-1 && sum == m) return 1;  // 剪枝
    if(dp[level][sum][sta] != -1) return dp[level][sum][sta];  // 已经搜索过的直接返回
    int res = 0;
    for(int i = 0 ; i < (1<<n) ; i ++) // 枚举第level+1层的状态
    {
        if(legal[i] && (i&sta) == 0 && ((sta<<1)&i) == 0 && ((sta>>1)&i) == 0)
            res += dfs(level+1,i,sum + id[i]);
    }
    dp[level][sum][sta] = res;
    return res;
}

signed main()
{
    memset(legal,0,sizeof(legal));
    memset(dp,-1,sizeof(dp));
    memset(id,0,sizeof(id));
    cin>>n>>m;
    for(int i = 0 ; i < 1024 ; i ++)	// 预处理出一层所有的合法状态
    {
        if(((i<<1)&i) == 0 && ((i>>1)&i) == 0)
            legal[i] = 1;
        int cnt = 0;
        for(int j = i ; j != 0 ; j >>= 1)
            if(j&1)     cnt ++;
        if(cnt > m)     legal[i] = 0;
        else            id[i] = cnt;
    }
    int ans = 0;
    for(int i = 0 ; i < (1<<n) ; i ++) // 枚举第1层状态
        if(legal[i])
            ans += dfs(0,i,id[i]);
    cout<<ans<<endl;
}

公開された104元の記事 ウォン称賛7 ビュー4072

おすすめ

転載: blog.csdn.net/qq_43461168/article/details/103481074