1. 程式人生 > >P1282 多米諾骨牌 【01揹包DP】

P1282 多米諾骨牌 【01揹包DP】

多米諾骨牌有上下2個方塊組成,每個方塊中有1~6個點。現有排成行的

上方塊中點數之和記為S1,下方塊中點數之和記為S2,它們的差為|S1-S2|。例如在圖8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。每個多米諾骨牌可以旋轉180°,使得上下兩個方塊互換位置。 程式設計用最少的旋轉次數使多米諾骨牌上下2行點數之差達到最小。

對於圖中的例子,只要將最後一個多米諾骨牌旋轉180°,可使上下2行點數之差為0。

輸入輸出格式

輸入格式:

輸入檔案的第一行是一個正整數n(1≤n≤1000),表示多米諾骨牌數。接下來的n行表示n個多米諾骨牌的點數。每行有兩個用空格隔開的正整數,表示多米諾骨牌上下方塊中的點數a和b,且1≤a,b≤6。

輸出格式:

輸出檔案僅一行,包含一個整數。表示求得的最小旋轉次數。

輸入輸出樣例

輸入樣例#1: 複製

4
6 1
1 5
1 3
1 2

輸出樣例#1: 複製

1

用DP【i】【j】來表示前 i 個數 差值為 j 的最小翻轉次數, 根據題目範圍可以知道差值在 -5000 到 5000 之間,但是陣列下標不能為負 ,所以平移一下,都+5000 平移到 0 到 10000

轉移方程見程式碼 分別代表翻與不翻

最後找出最小差值下最小的反轉次數,如果小於1000 就直接輸出 (因為最多翻轉1000次啊)

#include<bits/stdc++.h>
//#include <iostream>
//#pragma GCC optimize(2)
using namespace std;
#define maxn 1005
#define inf 1e18
#define eps 0.00001
typedef long long ll;
const ll mod = 1e9+7;
const double pi = acos(-1);

ll n,arr1[maxn],arr2[maxn],DP[maxn][11000],ans = inf;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);

    //freopen("D:\\test1.out","w",stdout);

    cin >> n;

    for(ll i = 1; i <= n; i++)
        cin >> arr1[i] >> arr2[i];

    memset(DP,0x3f3f,sizeof(DP));

    DP[0][5000] = 0;

    for(ll i = 1; i <= n; i++)
    {
        for(ll j = -5000; j <= 5000; j++)
        {
            ll temp = arr1[i] - arr2[i];
            DP[i][j+5000] = min( DP[i-1][ j+5000-temp ],DP[i-1][j+5000+temp]+1 );
        }
    }

    for(ll i = 0; i <= 5000; i++)
    {
        ans = min(DP[n][i+5000],DP[n][-i+5000]);
        if(ans < 1000)
        {
            cout << ans <<endl;
            return 0;
        }
    }

    //cout << ans << endl;

    return 0;
}