题目描述
多米诺骨牌有上下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
风格1:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int const N=1010; int const inf=0x3f3f3f3f; int a[N],b[N],n; int f[N][N*6*2];//表示差值+p为j时的最小次数 int main(){ memset(f,0x3f,sizeof(f)); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",a+i,b+i); int v=n*6*2; int p=n*6; f[0][p]=0; //以下部分是两种不同的区间平移 其实第二种看起来更清晰 而且第一种会出现下标越界的情况 但是不知道为何竟然A了 for(int i=1;i<=n;i++){ for(int j=0;j<=v;j++){ f[i][j]=min(f[i-1][j-(a[i]-b[i])],f[i-1][j-(b[i]-a[i])]+1); } } for(int i=1;i<=n;i++){ for(int j=-p;j<=p;j++){ f[i][j+p]=min(f[i-1][j+a[i]-b[i]+p],f[i-1][j+b[i]-a[i]+p]+1); } } // for(int i=0;i<=p;i++){ if(min(f[n][p+i],f[n][p-i])!=inf){ printf("%d\n",min(f[n][p+i],f[n][p-i])); return 0; } } }
风格2:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define ref(i,x,y)for(register int i=x;i<=y;i++) #define def(i,x,y)for(register int i=x;i>=y;i--) #define Q 2000 int a,b,n,nn,w[1010]; int f[1010][10100]; void first(){ scanf("%d",&n); ref(i,1,n) scanf("%d%d",&a,&b),w[i]=a-b; } void go(){ nn=5*n; memset(f,127,sizeof(f)); f[1][w[1]+nn]=0; f[1][-w[1]+nn]=1; ref(i,2,n) def(j,10*n,0){ if(j+w[i]>=0&&j+w[i]<=10*n) f[i][j]=min(f[i][j],f[i-1][j+w[i]]+1); if(j-w[i]>=0&&j-w[i]<=10*n) f[i][j]=min(f[i][j],f[i-1][j-w[i]]); } if(f[n][5*n]<Q) {printf("%d\n",f[n][5*n]);return;} else{ for(int i=nn-1,j=nn+1;i>=1&&j<=2*nn;i--,j++){ if(f[n][i]<Q||f[n][j]<Q){ printf("%d\n",min(f[n][i],f[n][j])); return; } } } } int main(){ first(); go(); return 0; }