轮廓线DP 专项

POJ 2411 Mondriaan’s Dream

题意: n ∗ m n*m nm(n,m<=11)的矩阵,填 1 ∗ 2 1*2 12 2 ∗ 1 2*1 21方块,求方案数

解析:

以前写过状压的做法,直接一行一行维护,现在写轮廓线的做法

轮廓线DP 专项_第1张图片

注意: 当前填红点的意思是,以红点作为方块的右下角

因为方块长度为2,所以对红点位置有三种操作

  1. 向上填:为了使所有方块填满,如果A方块没有被填,则必须向上填(红+A)
  2. 向左填:当上面已经填好时,而左边没有填时,可以向左填(红+B)
  3. 不填:当上面填好后,可以选择不填

细节:

  • 采用滚动数组,因为红点的状态只需要从B点转移,所以dp开二维滚动即可
  • 初始值:如果状态为0,那么第一行就要全部填向上的,显然是不符合的,所以初始状态为(11…11)
  • 不填的转移:若原状态为k,到了第j列不填,那么转移后的状态为:k^(1<<(j-1))(置0),因为对于下面的状态,这个位置空出来了
#include
#include
#include
#include
using namespace std;
#define LL long long
LL dp[2][2090];

int main(){
    int n,m;while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0)break;
        memset(dp,0,sizeof(dp));
        int maxState=(1<<m)-1;
        dp[0][maxState]=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int id=(i-1)*m+j;
                id&=1;
                memset(dp[id],0,sizeof(dp[id]));
                for(int k=0;k<=maxState;k++){
                    if(!dp[!id][k])continue;
                    if(!(k&(1<<(j-1))))dp[id][k|(1<<(j-1))]+=dp[!id][k];//只能往上填
                    else{
                        dp[id][k^(1<<(j-1))]+=dp[!id][k];//不填
                        if(j!=1&&!(k&(1<<(j-2)))){//可以左填
                            dp[id][k|(1<<(j-2))|(1<<(j-1))]+=dp[!id][k];
                        }
                    }
                }
            }
        }
        int id=n*m;id&=1;
        printf("%lld\n",dp[id][maxState]);
    }
}

   \\\;


   \\\;

HDU 1565 方格取数(1)

题意: 矩阵(n*n,n<=20)取一些数,使这些数的和最大,且任何两个数都不能相邻

解析:

和上题一模一样的思路,变成当上面和左边都空时才能插入,再把数组改大一点就过了。

当然,这里因为答案只会越来越大,所以就省略了memset的操作。但是为了当前的更新不干扰前面的状态,还是需要用滚动数组。

#include
#include
#include
#include
using namespace std;
#define LL long long
LL dp[2][1100000];

LL v[21][21];

int main(){
    int n,m;while(scanf("%d",&n)!=EOF){
        m=n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&v[i][j]);
            }
        }
        memset(dp,0,sizeof(dp));
        int maxState=(1<<m)-1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int id=(i-1)*m+j;
                id&=1;
                for(int k=0;k<=maxState;k++){
                    dp[id][k&(maxState-(1<<(j-1)))]=max(dp[id][k&(maxState-(1<<(j-1)))],dp[!id][k]);
                    if(!(k&(1<<(j-1)))&&!(k&(1<<(j-2))))dp[id][k|(1<<(j-1))]=max(dp[id][k|(1<<(j-1))],dp[!id][k]+v[i][j]);
                }
            }
        }
        int id=n*m;id&=1;
        LL ans=0;
        for(int i=0;i<=maxState;i++)ans=max(ans,dp[id][i]);
        printf("%lld\n",ans);
    }
}

   \\\;


   \\\;

HDU 2167 Pebbles

题意: 在上题的基础上,添加了对称相邻((1,1)和(2,2))也不能同时选的条件。而且蛋疼的是没有告诉n,需要自己从字符串得出来。

解析:

轮廓线DP 专项_第2张图片
也就是对于红点多了CD两个点的判断,但是比较麻烦的是,到了红点之后,状态k中记录的位置是B点的状态,所以需要再开拓一位记录C点的状态。而D点的状态是直接有记录的。

有个题外话, 虽然这两题没有用,前面拼矩阵需要所有都填满,所以初始状态为(11…11),但是这里前面对接下来的影响是不能相邻,所以如果有初始状态,应该是(00…00)。

#include
#include
#include
#include
using namespace std;
#define LL long long
LL dp[2][132000][2];//最后一维代表当前位置的上方位置的状态

LL v[16][16];
char x[100];

int data_in(){
    int n=0;
    int i=0,len=strlen(x),num=0;
    while(i<len){
        if(x[i]==' '){
            if(num){
                ++n;
                v[1][n]=num;
                num=0;
            }
        }
        else{
            num=num*10+x[i]-'0';
        }
        i++;
    }
    if(num){
        ++n;
        v[1][n]=num;
        num=0;
    }
    for(i=2;i<=n;i++){
        int data;
        for(int j=1;j<=n;j++){
            scanf("%d",&data);
            v[i][j]=data;
        }
    }
    return n;
}

void deal(int n){
    int maxState=(1<<n)-1;
    memset(dp,0,sizeof(dp));
    int id=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            id=!id;
            for(int k=0;k<=maxState;k++){
                for(int f=0;f<=1;f++){
                    //不选的情况
                    int to_f=0;
                    if(k&(1<<j-1))to_f=1;
                    dp[id][k&(maxState-(1<<j-1))][to_f]=max(dp[id][k&(maxState-(1<<j-1))][to_f],dp[!id][k][f]);

                    //选的情况
                    if(j==1){
                        if(!to_f&&!(k&(1<<j)))
                            dp[id][k|(1<<j-1)][to_f]=max(dp[id][k|(1<<j-1)][to_f],dp[!id][k][f]+v[i][j]);
                    }
                    else if(j==n){
                        if(!f&&!to_f&&!(k&(1<<j-2)))
                            dp[id][k|(1<<j-1)][to_f]=max(dp[id][k|(1<<j-1)][to_f],dp[!id][k][f]+v[i][j]);
                    }
                    else{
                        if(!to_f&&!(k&(1<<j))&&!f&&!(k&(1<<j-2)))
                            dp[id][k|(1<<j-1)][to_f]=max(dp[id][k|(1<<j-1)][to_f],dp[!id][k][f]+v[i][j]);
                    }
                }
            }
        }
    }
    LL ans=0;
    for(int i=0;i<=maxState;i++){
        for(int j=0;j<=1;j++){
            ans=max(ans,dp[id][i][j]);
        }
    }
    printf("%d\n",ans);
}

int main(){
    while(gets(x)!=0){
        int n=data_in();
        if(n==0)continue;//单回车行
        deal(n);
    }
    return 0;
}

你可能感兴趣的:(DP动态规划)