参考:点击打开链接
拿dp[i] 代表从当前状态能够获得的最大收益。
如果正在选择的那个人不能够在结束时获得大于等于一个魔法石,那么他的终极收益会变成min(-dp[下一个状态], dp[i]);
如果获得魔法石大于0 , dp[i] = max (dp[i], dp[i ^ (1<<j)] + flag);
#include <cstdio>
#include <iostream>
#include <cstring>
#define mst(a) memset(a,0,sizeof(a))
using namespace std;
int g,b,s;
const int INF = 0x3f3f3f3f;
int c[50][50];
int now[50];
int add;
int dp[(1<<21) + 10];
int ic[(1<<21) + 10][9];
int num, type;
void get_color()
{
for(int i = 1 ; i < (1 << b) ; i++)
{
for(int j = 0 ; j < b ; j++)
{
if( !(i & (1 << j) ))
{
for(int k = 1 ; k <= g ; k++)
{
ic[i][k] += c[j][k];
while(ic[i][k] >= s)
{
ic[i][k] -= s;
}
// cout << ic[i][k] << " ";
}
// cout << endl;
}
}
}
}
void mirror(int a)
{
for(int i = 0 ; i <= g ; i++) now[i] = ic[a][i];
}
void DP()
{
dp[0] = 0;
for(int i = 1; i < (1<<b) ; i++)
{
for(int j = 0; j < b ; j++)
{
if((1<<j) & i)
{
mirror(i);
int flag = 0;
for(int k = 0 ; k <= g ; k++)
{
now[k] += c[j][k];
while(now[k] >= s)
{
now[k]-=s;
flag++;
}
}
if(flag) dp[i] = max (dp[i], dp[i ^ (1<<j)] + flag);
else dp[i] = max (dp[i], 0 - dp[i ^ (1<<j)]);
}
}
}
}
int main()
{
while(scanf("%d%d%d", &g,&b,&s)!=EOF)
{
if(!g && !b && !s) return 0;
mst(c);
mst(ic);
for(int i = 0 ; i < (1<<b) ; i++) dp[i] = -INF;
for(int i = 0 ; i < b ; i++)
{
cin >> num;
for(int j = 0; j < num ; j++)
{
cin >> type;
c[i][type]++;
}
}
get_color();
DP();
cout << dp[(1<<b)-1] << endl;
}
return 0;
}