題目描述
多米諾骨牌有上下2個方塊組成,每一個方塊中有1~6個點。現有排成行的ios
上方塊中點數之和記爲S1,下方塊中點數之和記爲S2,它們的差爲|S1-S2|。例如在圖8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。每一個多米諾骨牌能夠旋轉180°,使得上下兩個方塊互換位置。 編程用最少的旋轉次數使多米諾骨牌上下2行點數之差達到最小。編程
先明確題意:
每一個物品有兩個狀態: 正着的和倒着的
求最小差值意義下的最小旋轉次數數組
首先看這個差值最小, 發現下值 = 總值 - 上值, 因而咱們統計一側和便可計算差值
而後設計一下狀態, 題目求最小旋轉次數, 那麼dp數組表示的應該是最小次數, 又有最小差值限制顯然要引入一維一側和做爲狀態
設計dp狀態的關鍵是看 以下狀態是否只對應一個最值
因而 \(dp[i][j]\) 表示考慮前 \(i\) 個, 上側和爲 \(j\) 的最小旋轉次數
邊界爲 第一個不轉 ---> \(dp[1][up[1]] = 0\)
轉 ---> \(dp[1][down[1]] = 1\)
注意當 \(up[1] == down[1]\) 時不用旋轉, 兩個值都爲 0優化
轉移直接看轉不轉便可, 相似 01揹包
注意 \(j\) 的狀態最大有 \(6n\)
而後考慮優化的話能夠讓最大狀態爲 \(\sum_{i = 1}^{n}max(up_{i}, down_{i})\)
還能夠滾動數組
懶就不寫了spa
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #include<algorithm> #include<climits> #define LL long long #define REP(i, x, y) for(int i = (x);i <= (y);i++) using namespace std; int RD(){ int out = 0,flag = 1;char c = getchar(); while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();} while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();} return flag * out; } const int maxn = 2019, inf = 1e9; int num; int a[maxn], b[maxn], sum; int dp[maxn][maxn * 6];//表示前i個骨牌,上面和爲j的最小交換數 void init(){ num = RD(); REP(i, 1, num)a[i] = RD(), b[i] = RD(), sum += a[i] + b[i]; REP(i, 1, num)REP(j, 1, num * 6)dp[i][j] = inf; if(a[1] == b[1])dp[1][a[1]] = dp[1][b[1]] = 0; else dp[1][a[1]] = 0, dp[1][b[1]] = 1; } void solve(){ REP(i, 1, num){ REP(j, 1, num * 6){//最多狀態數 if(dp[i][j] == inf)continue;//防越界就打了個刷表法 dp[i + 1][j + a[i + 1]] = min(dp[i + 1][j + a[i + 1]], dp[i][j]);//不轉 dp[i + 1][j + b[i + 1]] = min(dp[i + 1][j + b[i + 1]], dp[i][j] + 1);//轉 } } int minS = inf, ans; REP(i, 1, num * 6){ if(dp[num][i] == inf)continue; int now = abs((sum - i) - i); if(now < minS){ minS = now; ans = dp[num][i]; } else if(now == minS)ans = min(ans, dp[num][i]); } printf("%d\n", ans); } int main(){ init(); solve(); return 0; }