sgu 225 Little Knights 状态压缩dp

    这道题好无语呀,500ms时限,60m的内存。

    如果没有内存限制,我们可以很容易的想到  DP[U][V][ROW][KNIGHT]   U 代表上一行的状态,V代表上上一行的状态。因为当前行只与前两行的状态有关。ROW代表行数,knight代表骑士数目。

    然后dp就很简单了   DP【U】【V】【ROW】【KNIGHT】 = SUM(DP【V】【i】【ROW-1]【KNIGHT-CNT(i)】 ) i代表枚举的当前行的可行的状态(状态用一个整数表示),cnt(i)  就是求出状态i出现了多少个骑士

    可是这样开出的dp  就应该 是 DP【1024]【1024]【10]【101】,显然超出内存了。

    所以需要压缩,将前两行并成一个数。这样使得两行没冲突的合法状态最多又28000多个。然后 dp可以写成DP【29000】【10]【101】可是这样还是超出内存 ,然后在这里用滚动数组就ok了。由于我用的记忆化搜,写法不优越,10和9都不能很快出解。所以干脆打了个表。打出10 和 9的表之后  使得强两行无冲突的状态大大减少  DP应该是DP【10000】【10】【101】这个大小。

    跪求优越的写法呀~~~

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <string>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef double dl;
typedef vector<pii> vii;
typedef set<pii> sii;
/*sim_operarion*/
#define sfint(x) scanf("%d",&x)
#define sfint2(x,y) scanf("%d%d",&x,&y)
#define sfint3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sfstr(c) scanf("%s",c)
#define sfdl(x) scanf("%lf",&x)
#define pfint(x) printf("%d\n",x)
#define fr(i,s,n) for(int i=s;i<n;++i)
#define _fr(i,n,s) for(int i=n-1;i>=s;--i)
#define cl(a) memset(a,0,sizeof(a))

int n,k;
ll dp[10820][11][51];
int canuse[11000];
int tot;
int hash[1024*1024+1];
int cnt[1025];
ll list_10[110]={
    1,100,4662,135040,2732909,41199404,481719518,4491423916,34075586550,213628255072,1120204619108,4961681221524,18715619717199,60541371615660,168976761361446,409191804533576,864172675710439,1599730843649564,2609262108838924,3770687313420780,4857550050070531,5616928666465104,5874943705896600,5604501518609804,4917655076255841,3999855946779732,3034690618677388,2156485957257040,1437827591264317,899278231344296,526753407546620,288274613750624,146990556682887,69626509814580,30542906352994,12366448408056,4604442057431,1569983914256,487876545370,137395261280,34831261750,7884855000,1578162590,275861904,41455966,5246412,543534,44244,2652,104,2
};
ll list_9[82]={
    1,81,3016,68796,1080942,12472084,110018552,762775440,4241252429,19206532478,71707869632,222946143752,582155146204,1286247689414,2421159140764,3908273840366,5446391581062,6599640204257,7010436668992,6589213734278,5537849837497,4207779106033,2920161348852,1865346129716,1101125592067,600730512987,302041066250,139345014744,58692638521,22451454400,7755194754,2403337080,663103709,161373907,34237130,6238414,957145,120334,11914,872,42,1
};
int hashed(int x,int y) {
    return x*1024+y;
}
int lim;
int count(int x) {
    int ret = 0;
    fr(i , 0 ,n) if (1<<i & x) ++ret;
    return ret;
}
void init() {
    tot = 0;
    fr(i , 0 ,lim) {
        fr(j , 0 ,lim) {
            if (!(j<<2 & i || j>>2&i)) {
                int tmp = hashed(i,j);
                hash[tmp] = tot;
                canuse[tot++] = tmp;
            }
        }
    }
    fr(i , 0 ,lim) cnt[i] = count(i);
}

ll dfs(int u,int row,int knight) {
    if (dp[u][row][knight]!=-1) return dp[u][row][knight];
    int x=canuse[u]/1024,y=canuse[u]%1024;
    dp[u][row][knight] = 0;
    fr( i , 0 ,lim) {
        if (knight>=cnt[i]) {
            if (!(i<<1 & x  || i>>1&x || i<<2&y ||i>>2&y)) {
                if (row == 1) if (cnt[i]==knight) dp[u][row][knight]++;
                else  dp[u][row][knight] += dfs(hash[i+y*1024],row-1,knight-cnt[i]);
            }
        }
    }
    return dp[u][row][knight];
}


int main() {
    while(cin>>n>>k) {
        lim = 1<<n;
        if (n == 10) {
            cout<<list_10[k]<<endl;
            continue;
        }
        if (n==9){
            cout<<list_9[k]<<endl;
            continue;
        }
        init();
        memset(dp,-1,sizeof(dp));
        ll ans  = dfs(0,n,k);
        cout<<ans<<endl;
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值