题目大意
给你几个多米诺骨牌,可以上下翻转,求出最少的翻转次数使得上下相减的绝对值最小。
解题思路
首先,不考虑翻转次数,先考虑能翻到的最小绝对值,则定义一个
f
f
f的
b
o
o
l
bool
bool型数组,令
f
i
,
j
f_{i,j}
fi,j为前
i
i
i张牌能否造成绝对值为
j
j
j的方案,如果不翻转,则
f
i
,
j
=
f
i
−
1
,
j
−
(
a
[
i
]
−
b
[
i
]
)
f_i,_j=f_{i-1},_{j-(a[i]-b[i]})
fi,j=fi−1,j−(a[i]−b[i]),若翻转,就是由
a
i
−
b
i
a_i-b_i
ai−bi变为了
b
i
−
a
i
b_i-a_i
bi−ai。
初始化:前0张牌可以组成绝对值为0的方案。
其次,先考虑能翻出的最小绝对值,再考虑最少的翻转次数,我们可以将
f
[
i
,
j
]
f[i,j]
f[i,j]定义为前
i
i
i张骨牌构成
j
j
j的最小翻动次数,由此若是不翻转,则差依然是
a
i
−
b
i
a_i-b_i
ai−bi,翻动次数不变,若翻转,则差变为
b
i
−
a
i
b_i-a_i
bi−ai,翻动次数
+
1
+1
+1。由此可得:
f
[
i
,
j
]
=
m
i
n
(
f
[
i
–
1
,
j
–
(
a
[
i
]
–
b
[
i
]
)
]
,
f
[
i
–
1
,
j
–
(
b
[
i
]
–
a
[
i
]
)
]
+
1
)
f[i,j]=min(f[i–1,j–(a[i]–b[i])], f[i–1,j–(b[i]–a[i])]+1)
f[i,j]=min(f[i–1,j–(a[i]–b[i])],f[i–1,j–(b[i]–a[i])]+1)
(注意在开始要给f数组赋一个较大值,因为要求最小值,最后遍历时遇到的第一个不为初始值的值就是能翻出的最小绝对值)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int a[1050],n,b[1050],f[1050][12500],M=5050,maxn=0x7f,minn;
int main()
{
memset(f,0x5f,sizeof(f));//最好大一点,我前两次太小爆了
f[0][M]=0;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
for(int i=1;i<=n;i++)
{
for(int j=-M;j<=M;j++)//每一个可能的差值,注意这里不是绝对值
f[i][j+M]=min(f[i-1][j-(a[i]-b[i])+M],f[i-1][j-(b[i]-a[i])+M]+1);//因为数组不能有负数下标,所以手动加一个M让它成为非负数
}
for(int i=0;;i++)
{
if(f[n][M-i]<1600085855||f[n][M+i]<1600085855)//遍历到的第一个不是初始值的数就是能翻出的最小绝对值
{
cout<<min(f[n][M-i],f[n][M+i]);//因为是绝对值所以有正负两种情况
return 0;
}
}
return 0;
}