UVa 10817 Headmaster‘s Headache 【01背包 位运算】

本文介绍了一道来自刘汝佳训练指南的动态规划题目解析,通过设定dp数组来记录不同授课状态下的最小花费,并给出了具体的转移方程及完整代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:刘汝佳训练指南p95


题解:此题可以设dp[s2][s1] = 授课状态为s2、s1时的最小花费(s1为课程授课状态为1的集合,s2为授课状态为2的集合)

然后视招募的新老师为要装进背包里的物品,授课状态s2,s1为容量,最终答案就为dp[(1<<S)-1][(1<<S)-1](S为课程数目)

具体转移方程如下:

设s2,s1为招募该老师前的状态,ns2,ns1为招募该老师后的状态则,ns1 = s1 | s ,ns2 = s2 | (s1 & s) ,(s为该老师能教授的课程)

dp[ns2][ns1] = min(dp[s1 | s[i]][s2 | (s1 & s[i])]) + v[i],s[i]是第i个老师授课集合,v[i]是第i个老师的收费


#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include <string.h>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<time.h>
using namespace std;
#define MAX_N 505
#define inf 0x3f3f3f3f
#define LL long long
#define ull unsigned long long
const LL INF = 1e18;
const int mod = 1e8+7;
typedef pair<double, int>P;

int S, M, N;
int tC[25];
int aC[105];
int teacher[25];
int applicant[105];
int dp[1<<8][1<<8];
int getT()
{
    char c;
    int ans = 0;
    int d;
    c = getchar();
    while(c!='\n') {
        scanf("%d", &d);
        d--;
        ans |= (1<<d);
        c = getchar();
    }
    return ans;
}
void init()
{
    memset(dp, inf, sizeof(dp));
}
int main()
{
    while(cin >> S >> M >> N && S+M+N) {
        init();
        int s1 = 0;
        int s2 = 0;
        int sum = 0;
        for(int i=0; i<M; i++) {
            scanf("%d", &tC[i]);
            teacher[i] = getT();
            sum += tC[i];
            s2 |= (s1 & teacher[i]);
            s1 |= teacher[i];
        }
        for(int i=0; i<N; i++) {
            scanf("%d", &aC[i]);
            applicant[i] = getT();
        }
        dp[s2][s1] = sum;
        for(int k=0; k<N; k++) {
            int s = applicant[k];
            for(int i=(1<<S)-1; i>=0; i--) {
                for(int j=(1<<S)-1; j>=0; j--) {
                    int ns1 = j|s;
                    int ns2 = i|(j&s);
                    dp[ns2][ns1] = min(dp[ns2][ns1], dp[i][j]+aC[k]);
                }
            }
        }
        /*
       for(int i=0; i<(1<<S); i++) {
            for(int j=0; j<(1<<S); j++) {
                if(dp[i][j] == inf)
                    printf("0 ");
                else
                    printf("%d ",dp[i][j]);
            }
            puts("");
        }*/
        cout << dp[(1<<S)-1][(1<<S)-1] << endl;
    }
    return 0;
}
/*
8 4 3
1000 1
1000 2
1000 3
1000 2
50000 1 5 7 3 4
50000 1 6 8
50000 1 2 3 4 5 6 7 8
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值