1. 程式人生 > >洛谷 P1282 多米諾骨牌

洛谷 P1282 多米諾骨牌

gis clu 防止 pic gist load code http bsp

題目描述

多米諾骨牌有上下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

就是個背包的變形。。。

我們定義f[i][j]代表前I個多米諾骨牌的差值為j時的最小旋轉次數,那麽狀態轉移方程就很顯然了:

ab分別為一個骨牌的上下的點數,則

f[i][j]=min(f[i][j],min(f[i-1][j-(a-b)+5000],f[i-1][j-(b-a)+5000]+1))

不旋轉 旋轉

其中要加上5000就是要加上一個偏移量,防止下標為負數

初始化時將數組初始化為999999999f[0][5000]=0

查詢時就從5000開始向兩端擴展,若它們兩個取的f數組值不為999999999,就說明可以達到這個最小值,輸出這個次數即可。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<vector>
 8
#include<stack> 9 #include<queue> 10 #include<map> 11 #define RG register 12 #define IL inline 13 #define pi acos(-1.0) 14 #define ll long long 15 using namespace std; 16 int a[1005][3],n; 17 int w1[1005],w2[1005]; 18 int dp[1005][10005]; 19 int main() { 20 scanf("%d",&n); 21 for(int i=1;i<=n;i++){ 22 int k1,k2; 23 scanf("%d%d",&k1,&k2); 24 w1[i]=k1-k2; w2[i]=k2-k1; //w1不翻轉,w2翻轉 25 } 26 for(int i=0;i<=n;i++) 27 for(int j=0;j<=10002;j++) 28 dp[i][j]=999999999;//前I個多米諾牌,差值和為j的最小次數 29 dp[0][5000]=0; 30 for(int i=1;i<=n;i++) 31 for(int j=-5000;j<=5000;j++) 32 dp[i][j+5000]=min(dp[i][j+5000],min(dp[i-1][j-w1[i]+5000],dp[i-1][j-w2[i]+5000]+1)); 33 if(dp[n][5000]!=999999999){ 34 cout<<dp[n][5000]; 35 return 0; 36 } 37 int j=1,minn=999999999; 38 while(1){ 39 minn=min(dp[n][5000-j],dp[n][5000+j]); 40 if(minn!=999999999){ 41 cout<<minn; 42 return 0; 43 } 44 ++j; 45 } 46 return 0; 47 }

洛谷 P1282 多米諾骨牌