洛谷 P1282 多米诺骨牌 ( 线性DP )

题意 : 题目链接

 

分析 : 

一开始这个想法也有想到,但是貌似要开很大数组,就感觉应该不行

遂放弃想其他方法,万万没想到注意到可以滚动优化(其实不优化也可以过)

定义 dp[i][j] 表示 到第 i 个数为止,凑成 j 分数所要转的最小次数

转移方程如下

若选择旋转 i 这个多米诺 dp[i][j-Sub[i]] = min( dp[i][j-Sub[i]],  dp[i-1][j] )

若选择不旋转 dp[i][j+Sub[i]] = min( dp[i][j+Sub[i]], dp[i-1][j] + 1 )

但是有个问题,就是需要考虑第二维分数有负数的情况

加上一个偏移量就行了,转负为正

 

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
const int INF = 0x3f3f3f3f;
int dp[2][(6*maxn)<<1];
int L[maxn], R[maxn], Sub[maxn];

int main(void)
{
    int N, Tot = 0;
    scanf("%d", &N);
    for(int i=1; i<=N; i++)
        scanf("%d %d", &L[i], &R[i]),
        Sub[i] = L[i] - R[i],
        Tot += abs(Sub[i]);

    memset(dp, INF, sizeof(dp));
    dp[0][Tot] = 0;
    int idx = 1;

    for(int i=1; i<=N; i++){
        for(int j=0; j<=(Tot<<1); j++){

            if(j+Sub[i] >= 0 && j+Sub[i] <= (Tot<<1) )
                dp[idx][j+Sub[i]] = min(dp[idx][j+Sub[i]], dp[idx^1][j]);

            if(j-Sub[i] >= 0 && j-Sub[i] <= (Tot<<1) )
                dp[idx][j-Sub[i]] = min(dp[idx][j-Sub[i]], dp[idx^1][j]+1);

        }
        if(i != N){///滚动优化的话,每一层的初始都是 INF,所以对于下一层就要赋值为 INF
            idx ^= 1;
            for(int j=0; j<=(Tot<<1); j++)
                dp[idx][j] = INF;
        }
    }

    int ans = INF;
    for(int l=Tot,r=Tot; l>=0 && r<=(Tot<<1); l--,r++){///加上偏移量后 0 由 Tot 代替,从 0 开始左右扫,扫到第一个翻转次数不是初始值的便是答案
        if(dp[idx][l] != INF && dp[idx][r] != INF) ans = min(dp[idx][l], dp[idx][r]);
        else if(dp[idx][l] != INF) ans = dp[idx][l];
        else if(dp[idx][r] != INF) ans = dp[idx][r];
        if(ans != INF) break;
    }

    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2018-04-23 17:50  qwerity  阅读(153)  评论(0编辑  收藏  举报