1. 程式人生 > >多米諾骨牌題解

多米諾骨牌題解

題目描述

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

【題解】
將複雜的問題簡化了,根據題目要求,就是要求最小的翻轉步數使得所有骰子的上下差之和最小
這樣的描述很像是揹包問題的描述:在給定容量的揹包內放最大價值的物品。
唯一的差別就在於最小與給定,但這不影響做題
有了表層的理解我們不難仿造出類似揹包且是01揹包問題的狀態轉移方程
**f(i,j)=max{f(i1,j),f(i1,j2

(b[i]a[i])}
條件為(j2(b[i]a[i])0)初始條件為f[1][tot]=0,tot為初始狀態上下所有差之和
代表的意義是 前i個使得上下差之和為j的最小步數
關於(j2(b[i]a[i])0)可以很簡單地推匯出來:
初始是ab
那麼翻轉之後為ba
那麼兩者相差(ba)(ab)=2(ba)
證畢
所以程式碼以簡單地寫出來:

#include<cstdio>
#include<cstring>
#include<cstdlib> #include<cstring> #include<algorithm> #define inf 0x3f3f3f3f using namespace std; int n,a[1010],b[1010],f[2][15000],tot=0,sum=0; int main() { memset(f,inf,sizeof(f)); scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%d%d",&a[i],&b[i]),tot+=a[i]-b[i],sum+=abs(a[i]-b[i]); f[1][sum+tot]=0; tot=sum; tot<<=1; int t=0; for(int i=1; i<=n; i++,t^=1) { for(int j=0; j<=tot; j++) { f[t][j]=min(f[t][j],f[t^1][j]); if(j-2*(b[i]-a[i])>=0&&j-2*(b[i]-a[i])<=tot) f[t][j]=min(f[t][j],f[t^1][j-2*(b[i]-a[i])]+1); } } tot>>=1; for(int i=0; i<=tot; i++) { int k=min(f[t^1][tot-i],f[t^1][tot+i]); if(k!=inf) { printf("%d\n",k); break; } } return 0; }

這是除錯的程式碼:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int n,a[1010],b[1010],f[2][15000],tot=0,sum=0;


int main() {
    memset(f,inf,sizeof(f));
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&a[i],&b[i]),tot+=a[i]-b[i],sum+=abs(a[i]-b[i]);
    //printf("%d %d\n", sum, tot);
//  printf("%d %d\n",sum,tot);
    /*f[1][sum-(b[1]-a[1])*2]=1,*/f[1][sum+tot]=0;
    tot=sum;
    tot<<=1;
    int t=0;
//  puts("-------");
    for(int i=1; i<=n; i++,t^=1) {
        for(int j=0; j<=tot; j++) {
            f[t][j]=min(f[t][j],f[t^1][j]);
            if(j-2*(b[i]-a[i])>=0&&j-2*(b[i]-a[i])<=tot)
                f[t][j]=min(f[t][j],f[t^1][j-2*(b[i]-a[i])]+1);
        //  if(j+2*(b[i]-a[i])>=0&&j+2*(b[i]-a[i])<=tot)f[t][j+2*(b[i]-a[i])]=min(f[t][j+2*(b[i]-a[i])],f[t^1][j]+1); //翻
        //  if(j+2*(b[i]-a[i])>=0&&j+2*(b[i]-a[i])<=tot)printf("(%d,%d) ",j,j+2*(b[i]-a[i]));
        //  if(f[t][j]==inf)printf("inf ");
    //      else
        //  printf("%3d ",f[t][j]);
        }
    //  puts("");
    }
//  puts("-------");
    tot>>=1;
    for(int i=0; i<=tot; i++) {
        int k=min(f[t^1][tot-i],f[t^1][tot+i]);
        if(k!=inf) {
            printf("%d\n",k);
            break;
        }
    }
    return 0;
}