题目链接:http://acm.hust.edu.cn/vjudge/problem/19472
题意:有s个学科,学校里有m个老师,现在可以待聘用的老师有n个。给出每个老师的工资和可以教的学科,而且这m个老师不可以辞退,可以从这n个老师里面聘用任意个。求每门学科都至少有两个老师教的最小费用。
思路:用三进制压缩状态,表示每门科有0/1/2个老师教,首先这m个老师必须要的,所以先把起点状态st算出来。然后dp[state]表示当前教课状态为i时的最小花费,那么这个问题就变成了一个01背包,依次用第i个可以聘用的老师去更新dp数组即可。
#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
int s,m,n,st;
int K[10];//K[i] = i^3
int f[7000][10]; //f[i][j] i在三进制下的第j位
int dp[7000];
char str[500];
//待聘用老师的工资和每个人教的学科
int cost[109];
vector<int> g[109];
void init()
{
K[0] = 1;
rep(i,1,9)
K[i] = K[i-1] * 3;
int uplim = K[8];
rep(i,0,uplim-1)
{
int temp = i;
Rrep(j,7,0)
{
f[i][j] = 0;
while( temp >= K[j] ) temp-=K[j] , f[i][j]++;
}
}
}
bool read( int &pos , int &k )
{
if ( str[pos] == '\0' ) return false;
while( str[pos] == ' ' ) pos++;
k = 0;
while( str[pos]>='0' && str[pos] <= '9' )
{
k = k * 10 + str[pos] - '0';
pos++;
}
return true;
}
int unio( int state , int pos ) //当前教课的状态+第pos个老师聘用后教课的状态
{
int up = g[pos].size() - 1;
rep(i,0,up)
if ( f[state][g[pos][i]-1] < 2 ) //如果这门课人数少于2,就加1,多余2还按照2记录
state += K[g[pos][i]-1];
return state;
}
void solve()
{
int uplim = K[s] - 1;
rep(j,1,n)
Rrep(i,uplim,st)
{
int news = unio( i , j );
dp[news] = min( dp[news] , dp[i] + cost[j] );
}
printf("%d\n",dp[uplim]);
}
int main()
{
init();
while( cin>>s>>m>>n )
{
if ( s == 0 ) break;
st = 0;
int ans = 0;
Clean(dp,0x3f);
getchar();
rep(i,1,m) //读入已有老师的信息
{
int temp;
gets(str);
int x = 0;
read(x,temp);
ans += temp; //工资
while( read(x,temp) )
{
if ( f[st][temp-1] < 2 ) st += K[temp-1];
}
}
dp[st] = ans;//初始状态
rep(i,1,n)
{
int temp,pos = 0;
gets(str);
read(pos,temp);
cost[i] = temp;
g[i].clear();
while( read(pos,temp) )
{
g[i].push_back(temp);
}
}
solve();
}
return 0;
}