UVA10118
定义:
d
p
[
a
]
[
b
]
[
c
]
[
d
]
dp[a][b][c][d]
dp[a][b][c][d]为第0列取了
a
a
a颗糖果,第1列
b
b
b颗,第二列
c
c
c颗,第三列
d
d
d后剩下的糖果还能最多再装进口袋多少个。那么答案就是
d
p
[
0
]
[
0
]
[
0
]
[
0
]
dp[0][0][0][0]
dp[0][0][0][0],即考虑所有糖果。
C
a
n
d
i
e
s
[
i
]
[
j
]
Candies[i][j]
Candies[i][j]表示第
i
i
i行第
j
j
j列的糖果颜色。
初始化:
d
p
=
−
1
dp=-1
dp=−1
表示未计算过
转移方程:
d
p
[
a
]
[
b
]
[
c
]
[
d
]
=
m
a
x
(
d
p
[
a
+
1
]
[
b
]
[
c
]
[
d
]
+
I
s
P
a
i
r
(
0
,
a
+
1
)
,
d
p
[
a
]
[
b
+
1
]
[
c
]
[
d
]
+
I
s
P
a
i
r
(
1
,
b
+
1
)
,
d
p
[
a
]
[
b
]
[
c
+
1
]
[
d
]
+
I
s
P
a
i
r
(
2
,
c
+
1
)
,
d
p
[
a
]
[
b
]
[
c
]
[
d
+
1
]
+
I
s
P
a
i
r
(
3
,
d
+
1
)
)
dp[a][b][c][d]=max(\\dp[a+1][b][c][d]+IsPair(0,a+1),\\dp[a][b+1][c][d]+IsPair(1,b+1),\\dp[a][b][c+1][d]+IsPair(2,c+1),\\dp[a][b][c][d+1]+IsPair(3,d+1)\\)
dp[a][b][c][d]=max(dp[a+1][b][c][d]+IsPair(0,a+1),dp[a][b+1][c][d]+IsPair(1,b+1),dp[a][b][c+1][d]+IsPair(2,c+1),dp[a][b][c][d+1]+IsPair(3,d+1))
I
s
P
a
i
r
(
C
o
w
,
R
a
w
)
IsPair(Cow,Raw)
IsPair(Cow,Raw)表示拿了
C
a
n
d
i
e
s
[
C
o
l
]
[
R
a
w
]
Candies[Col][Raw]
Candies[Col][Raw]后是否能和篮子中的糖果配对,并放入口袋中。
AC代码:
代码中以
R
a
w
[
0
]
Raw[0]
Raw[0]表示
a
a
a,
R
a
w
[
1
]
Raw[1]
Raw[1]表示
b
b
b,
R
a
w
[
2
]
Raw[2]
Raw[2]表示
c
c
c,
R
a
w
[
3
]
Raw[3]
Raw[3]表示
d
d
d,以减少代码量。采用二进制压缩的方式表示当前篮子中的糖果集合。
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
#define scanf scanf_s
int N;
int Candies[41][4];
int dp[41][41][41][41];
bool Input() {
cin >> N;
if (!N) {
return false;
}
for (int i = 1; i <= N; ++i) {
for (int j = 0; j < 4; ++j) {
cin >> Candies[i][j];
}
}
return true;
}
//获取篮子中糖果的数量
int getNumber(int CandiesSet) {
int&& Number = 0;
while (CandiesSet) {
Number += (CandiesSet & 1);
CandiesSet >>= 1;
}
return Number;
}
int DP(int Raw[4], int CandiesSet/*篮子中糖果的集合*/) {
int& Ans = dp[Raw[0]][Raw[1]][Raw[2]][Raw[3]];
//如果计算过就直接返回
if (~Ans) {
return Ans;
}
//糖果数量大于4就说明篮子满了,返回极小值
if (getNumber(CandiesSet) > 4) {
return Ans = 0xcfcfcfcf;
}
//最少装0个
Ans = 0;
//枚举a+1,b+1,c+1,d+1
for (int i = 0; i < 4; ++i) {
//如果到了最后一列最跳过,不能再往下了
if (Raw[i] != N) {
int&& Total = 0;
//+1
++Raw[i];
//向下取的那个糖果
int&& CurrentCandy = 1 << (Candies[Raw[i]][i] - 1);
//如果新取的糖果再篮子集合中,则可以配对
if (CandiesSet & CurrentCandy) {
++Total;
}
//往下DP
Total += DP(
Raw,
//如果新取的糖果在篮子中,就配对成功,集合去点这种糖果,如果没有,就将糖果加入篮子中(异或的开关性)
CandiesSet ^ CurrentCandy
);
//还原Raw[i],为枚举下一列做准备
--Raw[i];
Ans = max(Ans, Total);
}
}
return Ans;
}
int main() {
ios::sync_with_stdio(false);
while (Input()) {
memset(dp, -1, sizeof(dp));
int Raw[4] = { 0,0,0,0 };
cout << DP(Raw, 0) << endl;
}
return 0;
}