多米諾骨牌題解
阿新 • • 發佈:2019-02-18
題目描述
多米諾骨牌有上下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揹包問題的狀態轉移方程
**
條件為初始條件為,tot為初始狀態上下所有差之和
代表的意義是 前i個使得上下差之和為j的最小步數
關於可以很簡單地推匯出來:
初始是
那麼翻轉之後為
那麼兩者相差
證畢
所以程式碼以簡單地寫出來:
#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;
}