题目:
显而易见的背包问题+集合动归
关键是集合不是一般的集合,每门课的状态有三种:没有老师,有了1个老师,有两个及以上。
1.我的办法是用3进制数表示
2.看了别人的题解
dp[state1][state2],只有当state1里面对应位置被填充了,才能填充state2
nextS1 = p[i] | s1,
nextS2 = (p[i] & s1) | s2
f[nextS1][nextS2] = min(f[nextS1][nextS2], f[s1][s2] + p[i])
感觉这种方非常简便。
但是他这种做法的时间复杂度比我要高一点。因为 2^8*2^8=65 536>3^8=6 561
他的状态比我多一些。
/**==========================================
* This is a solution for ACM/ICPC problem
*
* @source: uva 10817 Headmaster¡¯s Headache
* @type: dp
* @author: wust_ysk
* @blog: http://blog.csdn.net/yskyskyer123
* @email: 2530094312@qq.com
*===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<sstream>
#include<vector>
#define ysk(x) (1<<(x))
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 120 ;
//const int maxV=12 ;
int sub,m,n;
string s;
int sal[maxn+5];
vector<int> ab[maxn+5];
const int maxed= 6561-1;//3^8
int fac[]={1,3,9,27,81,243,729,2187,6561};
int dp[ maxed+5],ed;
int get(int s,int x)
{
s/=fac[x];
return s%3;
}
int addst(int s,int x)
{
int num=get(s,x);
if(num==2) return s;
s+=fac[x];
return s;
}
int main()
{
int x;
while(~scanf("%d%d%d",&sub,&m,&n)&&(m+n+sub))
{
getchar();
int st=0,ret=0;
ed=fac[sub]-1;
for(int i=1;i<=m+n;i++)
{
getline(cin,s);
stringstream ss(s);
ss>>sal[i];
if(i<=m) ret+=sal[i];
ab[i].clear();
while(ss>>x)
{
x--;
ab[i].push_back(x);
if(i<=m) st=addst( st,x);
}
}
memset(dp,0x3f,fac[sub] *sizeof dp[0]);
dp[st]=ret;
for(int i=m+1;i<=m+n;i++)
{
for(int s=ed;s>=st;s--)
{
int s2=s;
for(int j=0;j<ab[i].size();j++)
{
int x=ab[i][j];
s2=addst(s2,x);
}
dp[s2]=min(dp[s2],dp[s]+sal[i]);
}
}
printf("%d\n",dp[ed]);
}
return 0;
}