1. 程式人生 > >BZOJ1725】[Usaco2006 Nov]Corn Fields牧場的安排 狀壓DP

BZOJ1725】[Usaco2006 Nov]Corn Fields牧場的安排 狀壓DP

close sizeof table return cin 思考 memset std 關系

Description

Farmer John新買了一塊長方形的牧場,這塊牧場被劃分成M列N行(1<=M<=12; 1<=N<=12),每一格都是一塊正方形的土地。FJ打算在牧場上的某幾格土地裏種上美味的草,供他的奶牛們享用。遺憾的是,有些土地相當的貧瘠,不能用來放牧。並且,奶牛們喜歡獨占一塊草地的感覺,於是FJ不會選擇兩塊相鄰的土地,也就是說,沒有哪兩塊草地有公共邊。當然,FJ還沒有決定在哪些土地上種草。 作為一個好奇的農場主,FJ想知道,如果不考慮草地的總塊數,那麽,一共有多少種種植方案可供他選擇。當然,把新的牧場荒廢,不在任何土地上種草,也算一種方案。請你幫FJ算一下這個總方案數。

Input

* 第1行: 兩個正整數M和N,用空格隔開

* 第2..M+1行: 每行包含N個用空格隔開的整數,描述了每塊土地的狀態。輸入的第i+1行描述了第i行的土地。所有整數均為0或1,是1的話,表示這塊土地足夠肥沃,0則表示這塊地上不適合種草

Output

* 第1行: 輸出一個整數,即牧場分配總方案數除以100,000,000的余數

Sample Input

2 3
1 1 1
0 1 0

Sample Output

9

解題思路 :以樣例數據第一行為例,三個格子都可以放牧,即每個格子都可以選擇放,或不放。再考慮附加條件“相鄰格子不可同時放牧”,那麽我們可以列出單看第一行時的所有可行狀態如下(1代表放牧,0代表不放牧)

編號 狀態
1 0 0 0
2 0 0 1
3 0 1 0
4 1 0 0
5 1 0 1
(表1)

由此,可將表中的狀態看作二進制表示,那麽,只需將每種狀態轉化為相應的十進制數,即可只用一個數字,就能表示某一種狀態,如下表:

編號 二進制 十進制
1 0 0 0 0
2 0 0 1 1
3 0 1 0 2
4 1 0 0 4
5 1 0 1 5
(表2) 這種用一個數來表示一組數,以降低表示狀態所需的維數的解題手段,就叫做狀態壓縮。

至此我們看到,在只考慮第一行的時候,有5種可行的放牧方案,但這只是我們要做的第一步。接下來要將第二行納入考慮:

首先思考:納入第二行後,會對當前問題造成什麽樣的影響?

答案還是那句話:“ 相鄰格子不可同時放牧 ”!

也就是說,不止左右相鄰不可以,上下之間也不能存在相鄰的情況。

首先觀察第二行,只有中間的格子可以放牧,那麽我們的狀態表格就可以相對簡單些了~如下:

編號 二進制 十進制
1 0 0 0 0
2 0 1 0 2
(表3) 只有兩種可行狀態,那麽我們不妨一個一個來考察:

1、當第二行的狀態為編號1時,第二行的三個格子都沒有放牧,那麽就不會與第一行的任何情況有沖突,第一行的5種方案都可行,即:第二行選用編號1的狀態時,結合第一行,可得到5種可行的放牧方案;

2、當第二行的狀態為編號2時,第二行中間的格子已經放牧了,那麽第一行中間的格子就不可以放牧。看表2,發現其中第3種狀態與當前第二行沖突,那麽第一行只有4種方案是可行的,即:第二行選用編號2的狀態時,結合第一行,可得到4種可行的放牧方案;

那麽,在樣例數據給出的情況下,我們的最終答案即為5+4=9;

通過對樣例數據的分析即可以發現不同狀態之間的關系:

以 dp[i][state(j)] 來表示對於 前i行 , 第i行 采用 第j種狀態 時可以得到的 可行方案總數!

例如:回頭看樣例數據,dp[2][1]即代表第二行使用第2中狀態(0 1 0)時可得的方案數,即為4;

那麽,可得出狀態轉移方程為:

dp[i][state(j)]=dp[i-1][state(k1)]+dp[i-1][state(k2)]+......+dp[i-1][state(kn)] (kn即為上一行可行狀態的編號,上一行共有n種可行狀態)

最終ans=dp[m][state(k1)]+dp[m][state(k2)]+......+dp[m][state(kn)]; (kn即為 最後一 行 (第m行) 可行狀態的編號)

AC代碼:

技術分享圖片
#include<stdio.h>
#include<string.h>
const int mod = 100000000;
int n,m,tot,v[20],ans;//v[i]//第i行整行的情況
int dp[20][520],s[520];//dp對於前i行,每行有前j種可能狀態時的解
//s[i]存儲每行所有可行的狀態
int main( )
{
    int a;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
           tot=ans=0;
    memset(dp,0,sizeof(dp));
    memset(s,0,sizeof(s));
    memset(v,0,sizeof(v));

    for(int i=0 ; i<1<<m ; i++)
    if((i&(i<<1))==0)///記錄不相鄰的狀態
    s[++tot]=i;
    for(int i=1 ; i<=n ; i++)
    {
        for(int j=1 ; j<=m ; j++)
        {
            scanf("%d",&a);
            if(a==0)
            v[i]+=1<<j-1;//相反方式存儲
        }
    }
    dp[0][1]=1;
    for(int i=1 ; i<=n ; i++)
    {
        for(int j=1 ; j<=tot ; j++)//判斷第i行 假如按狀態j放牛的話行不行
        {
            if(s[j]&v[i])//剪枝 判斷上一行與其狀態是否滿足
            continue;
            for(int k=1 ; k<=tot ; k++)
            {
                if(s[j]&s[k])
                continue;
                dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
            }
        }
    }

        for(int i=1;i<=tot;i++)
    {
        if(s[i]&v[n])    continue;
        ans=(ans+dp[n][i])%mod;
    }
    printf("%d\n",ans);
    }
    return 0;
}
View Code

BZOJ1725】[Usaco2006 Nov]Corn Fields牧場的安排 狀壓DP