UVA10817 校长的烦恼 Headmaster’s Headache(记忆化搜索+状压dp)

 

题意:

题目描述 斯普林菲尔德(春之田野???)学院的校长正在考虑为某些科目招聘新老师 现在有一批申请岗位的老师 每个老师能教一个或多个科目 校长想要选择一些申请者以保证每个科目都至少有两名老师能教 同时总共花的钱要最小

输入格式: 输入由多组测试组成,它们的格式如下: 第一行包含三个给定的整数S,M和N, S (<=8)是科目的总数 M(<=20)是在职教师数,N(<=100)是应聘者的数量

接下来M行每行描述一个在职教师 首先给出雇佣他的花费C (10000<=C<=50000) ,接下来是他能教的科目列表 科目用1—S的整数表示 你必须保证继续聘用他们

之后N行,以同样格式给出应聘者的信息

输入以一个空测试S=0结束,你不应当处理它 (这句有些别扭,不过懂意思就好)

思路:

 科目的总数只有$8$,考虑把每个老师会哪几门课这个条件转化为二进制数表示。

然后记忆化搜索:$dp[i][s0][s1][s2]$表示枚举到第$i$个人时,没有人教的课程的二进制数表示为$s0$,有一个人教的课程表示为$s1$,有两个人教的课程表示为$s2$.

当枚举到第$i$个人并且$i>=m$时(所有的下标从$0$开始),可以不选择这个人。

对于所有的教师都可以选择这个人,考虑这个人被选后会对记忆化搜索的变量产生哪些影响。

1.记录别人都不会但是第$i$个人会的课程

2.记录别人有一个人会而且第$i$个人也会的课程。

3.

s0=s0^m0;///在没人会的集合里面去除i会的
s1=(s1^m1)|m0;///首先在一个人会的集合里去除i也会的,然后加上只有i会的,这是所有一个人会的
s2=s2|m1;///现在相当于两个人会

 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
typedef pair<double, double>PDD;
#define I_int ll
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
#define read read()
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
ll ksm(ll a, ll b, ll p)
{
    ll res = 1;
    while(b)
    {
        if(b & 1)res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
const int inf = 0x3f3f3f3f;
#define PI acos(-1)
const int maxn=1000000000;

int s,n,m,val[150],st[150];
int dp[150][1<<8][1<<8];

int dfs(int i,int s0,int s1,int s2){
    if(i==n+m){
        return s2 == (1 << s) - 1 ? 0 : inf;
    }
    int &res=dp[i][s1][s2];
    if(res>=0) return res;
    res=inf;
    if(i>=m){
        res=dfs(i+1,s0,s1,s2);
    }
    int m0=st[i]&s0;///别人都不会 i会
    int m1=st[i]&s1;///只有一个人会 i会
    s0=s0^m0;///在没人会的集合里面去除i会的
    s1=(s1^m1)|m0;///首先在一个人会的集合里去除i也会的,然后加上只有i会的,这是所有一个人会的
    s2=s2|m1;///现在相当于两个人会
    res=min(res,dfs(i+1,s0,s1,s2)+val[i]);
    return res;
}

int main()
{

    string str;
    while(getline(cin,str)){
        stringstream ss;
        ss<<str;
        ss>>s>>m>>n;
        if(!s) break;

        rep(i,0,m+n-1){
            getline(cin,str);
            stringstream ss;
            ss<<str;
            ss>>val[i];///工资
            st[i]=0;
            int x;
            while(ss>>x){
                x--;
                st[i]|=(1<<x);
            }
        }
       
        memset(dp,-1,sizeof dp);
        printf("%d\n",dfs(0,(1<<s)-1,0,0));
    }

    return 0;
}

/*

**/
View Code

 

posted @ 2021-06-07 15:36  OvO1  阅读(80)  评论(0编辑  收藏  举报