UVA 10817 Headmaster's Headache

题目链接: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;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值