UVA 10817 Headmaster's Headache (状压dp+记忆化搜索)

题意:这间学校开设S门课,给出校长已经有的师资n,然后再给出m个应聘者,每门课至少有两名任课老师,求最少需要的雇佣工资。

分析:一开始没什么头绪,看到网上说01背包,一想是那么回事。还有一个技巧是把2*s位的二进制数表示状态,假设有两门课,那么1100表示两门课都有两个以上老师,0011表示这两门课都只有一个老师。根据现有老师算出初始状态,然后转移,转移终点是到达1100的状态(假设有两门课)。个人感觉记忆化搜索比循环转移好用多了,清晰明了。

因为st数组没初始化,卡了3个小时。。。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
#define FRER() freopen("in.txt","r",stdin)
#define FREW() freopen("out.txt","w",stdout)
#define go int T;cin>>T;while(T--)
#define debug cout<<"****************"<<endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn = 120 + 7,inf =0x3f3f3f3f;
int cost[maxn],st[maxn],dp[maxn][1<<16],cnt[10];
int n,m,s,t;
int chageST(int s1,int s2){
    for(int i=0;i<s;i++){
        if((s2&(1<<i))){
            if((s1&(1<<(s+i)))){
                continue;
            }else{
                if((s1&(1<<i))){
                    s1-=(1<<i);
                    s1+=(1<<(i+s));
                }else{
                    s1+=(1<<i);
                }
            }
        }
    }
    return s1;
}
int dfs(int i,int nowst){
    if(dp[i][nowst]!=-1) return dp[i][nowst];
    int& ans = dp[i][nowst];
    if(nowst==t) return ans =  0;
    if(i==0) return  ans = inf;
    ans = inf;
    int ans1 = dfs(i-1, nowst);
    int ans2 = dfs(i-1, chageST(nowst, st[i]))+cost[i];
    return ans = min(ans,min(ans1,ans2));
}
int main(){
    //FRER();
    //FREW();
    while(~scanf("%d%d%d",&s,&m,&n)&&(n||m||s)){
        memset(dp, -1, sizeof(dp));
        memset(cnt, 0, sizeof(cnt));
        memset(st, 0, sizeof(st));
        int sum = 0;
        for(int i=0;i<m;i++){
            int c;char ch;
            scanf("%d",&c);
            sum+=c;
            while(1){
                scanf("%d%c",&c,&ch);
                //cout<<c<<endl;
                c--;
                cnt[c]++;
                if(ch=='\n') break;
            }
        }
        for(int i=1;i<=n;i++){
            int c;char ch;
            scanf("%d",&cost[i]);
            while(1){
                scanf("%d%c",&c,&ch);
               // cout<<c<<endl;
                c--;
                st[i] += (1<<c);
                if(ch=='\n') break;
            }
        }
        int now = 0;
        for(int i=0;i<s;i++){
            if(cnt[i]>=2){
                now |= 1<<(s+i);
            }else if(cnt[i]==1){
                now |= 1<<i;
            }
        }
        t =(1<<(2*s))-(1<<s);
        if(now==t){
            cout<<sum<<endl;
        }else{
            printf("%d\n",dfs(n,now)+sum);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值