UVA10118
定义:
为第0列取了
颗糖果,第1列
颗,第二列
颗,第三列
后剩下的糖果还能最多再装进口袋多少个。那么答案就是
,即考虑所有糖果。
表示第
行第
列的糖果颜色。
初始化:
表示未计算过
转移方程:
表示拿了
后是否能和篮子中的糖果配对,并放入口袋中。
AC代码:
代码中以
表示
,
表示
,
表示
,
表示
,以减少代码量。采用二进制压缩的方式表示当前篮子中的糖果集合。
#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;
}