1. 程式人生 > >HDU1565:方格取數(1) (狀態壓縮DP)

HDU1565:方格取數(1) (狀態壓縮DP)

Problem Description
給你一個n*n的格子的棋盤,每個格子裡面有一個非負數。
從中取出若干個數,使得任意的兩個數所在的格子沒有公共邊,就是說所取的數所在的2個格子不能相鄰,並且取出的數的和最大。
 

Input
包括多個測試例項,每個測試例項包括一個整數n 和n*n個非負數(n<=20)
 

Output
對於每個測試例項,輸出可能取得的最大的和
 

Sample Input
3
75 15 21
75 15 28
34 70 5
 

Sample Output
188
 

與HDU1074有點相似,還是用二進位制來記錄表示取或者不取,一行行的進行計算,用一個數組記錄上一行的所有取法,一個數組記錄現在這行的所有取法,如果取法進行與運算為1的話,那麼就代表有相鄰的而不進行計算,然後這樣一直DP到最後一行便可以得到最後的結果

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

const int L = 20000;
int n,a[20][20];
int dp[L],tem[L];
int now[L],pre[L];
int ans[L],pre_size,now_size;

void dfs(int id,int k,int p,int sum)
{
    if(k>=n)//超過n則可以記錄這個狀態
    {
        now[++now_size] = p;
        ans[now_size] = sum;
        return ;
    }
    dfs(id,k+2,p|(1<<k),sum+a[id][k]);//這個位置取了,那麼就要加上這個位的二進位制,通過或運算得出,這個位置取了的話,那麼下一個要取的至少要跳兩格
    dfs(id,k+1,p,sum);//這個位置不取並跳一格
}

void DP()
{
    int i,j,k;
    for(k = 1; k<=n; k++)
    {
        now_size = 0;
        dfs(k,0,0,0);//搜出此行的所有狀態
        for(i = 1; i<=now_size; i++)
            dp[i] = 0;
        for(i = 1; i<=now_size; i++)
        {
            for(j = 1; j<=pre_size; j++)
            {
                if(now[i]&pre[j]) continue;//相與為1,證明有相鄰而不繼續往下
                dp[i] = max(dp[i],tem[j]+ans[i]);
            }
        }
        for(i = 1; i<=now_size; i++)//目前這行的狀態儲存為上一行
        {
            tem[i] = dp[i];
            pre[i] = now[i];
        }
        pre_size = now_size;
    }
}

int main()
{
    int i,j;
    while(~scanf("%d",&n))
    {
        for(i = 1; i<=n; i++)
            for(j = 0; j<n; j++)
                scanf("%d",&a[i][j]);
        tem[1] = pre[1] = 0;//先取一個,但是位置並不確定,
        pre_size = 1;
        DP();
        int ans = 0;
        for(i = 1;i<=pre_size;i++)
        ans = max(ans,tem[i]);
        printf("%d\n",ans);
    }

    return 0;
}