题目链接:http://acm.hust.edu.cn/vjudge/problem/19440
题意:一个人取四堆都为n的糖果,糖果的种类为1~20,他有一个可以最多装五颗糖的小篮子,如果篮子里有两颗一样的糖果,那么就可以把这一对糖果拿走。问最多可以得到多少对糖果。
思路:记忆化搜索dp[a][b][c][d]表示这四堆糖果分别取走了a,b,c,d个时得到的最大对数。对于dp[a][b][c][d]这个状态,枚举取糖果的堆,递归记忆化搜索取结果最大的情况。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod 100000007
const int maxn = 42;
int a[maxn][maxn];
int dp[maxn][maxn][maxn][maxn];
bool choose[30]; //每种糖果是否已经在篮子里了
int n;
int dfs( int pos[] , int num )
{
int &ans = dp[pos[0]][pos[1]][pos[2]][pos[3]];
if( ans != -1 ) return ans;
if ( num == 5 ) return ans = 0; //篮子满了
ans = 0;
rep(i,0,3)
{
if ( pos[i] == n ) continue;//当前堆取完了
pos[i]++;
if ( choose[ a[pos[i]][i] ] ) //选过当前糖果
{
choose[ a[pos[i]][i] ] = false;
ans = max( ans , dfs( pos , num - 1 ) + 1 ); //自己取到了一对糖果
choose[ a[pos[i]][i] ] = true;
}
else //之前没选过
{
choose[ a[pos[i]][i] ] = true;
ans = max( ans , dfs( pos , num + 1 ) );
choose[ a[pos[i]][i] ] = false;
}
pos[i]--;
}
return ans;
}
int main()
{
while( cin>>n )
{
if ( !n ) break;
rep(i,1,n)
rep(j,0,3) scanf("%d",&a[i][j]);
Clean(dp,-1);
Clean(choose,false);
int pos[4] = {0,0,0,0};
printf("%d\n",dfs(pos,0));
}
return 0;
}