洛谷 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; }