校长的烦恼 [状态压缩][DP]

校长的烦恼 [状态压缩][DP]

问题描述

某中学开设有s门课程,现有教师m个。今天有n个求职者来应聘新教师。
已知每个人工资和能教授的课程。
在职教师不能辞退,校长想知道,最少支付多少工资就能使得每门课都有至少两名教师能教。

输入格式

第一行,三个整数s,m和n
接下来m行,每行若干个整数,描述一名在职教师的情况:第一个整数,表示该教师的工资,接下来的若干整数表示该教师能教的课程;
接下来n行,每行若干个整数,描述一名求职者的情况:第一个整数,表示该求职者要求的工资,接下来的若干整数表示该求职者能教的课程;
注意,每行末尾有空格

输出格式

一行,一个整数,表示所求结果。
若无解,输出-1

设定一个状态f[i][j],其中i表示至少有一个老师能教这门课,j表示至少有两个老师能教这门课,f[i][j]表示当前状态的最少工资。

最初的状态就是所有在职教师的所在课程和工资。

接下来就从每个课程开始枚举,即从所有课程都至少有两名老师教的状态开始枚举(因为每个来应聘的教师只能够用一次,所以要倒着枚举)。
对于每门课程(不管它现在有几个人教),假设起用第k个应聘者,那么他对当前状态f[i][j]的影响如下:

对于i,显然有A=i|k。对于j,有B=(i&k)|j,因为i和k共同能教的课一定至少有两个老师能教,然后这些课加进j。

所以说,f[A][B]=min{ f[i][j]+Cost[k] }

答案输出f[1<<s-1][1<<s-1]

这其实和背包很相似,只是用状态压缩的方式实现。

#include<iostream>
#include<cstdio>
#define N 500
using namespace std;
int f[N][N],cnt[N],Cost[N],New[N],A,B;
void Get1(){
    int x=0;char c;
    while(((c=getchar())<'0' || c>'9') && c!='\n');
    x=c-'0';
    while(1){
        while((c=getchar())>='0' && c<='9')x=(x<<3)+(x<<1)+c-'0';
        cnt[x]++,A|=(1<<(x-1));
        while(!((c=getchar())>='0' && c<='9') && c!='\n');
        if(c=='\n' || c==EOF)return;
        x=c-'0';
    }
}
void Get2(int i){
    int x=0;char c;
    while(((c=getchar())<'0' || c>'9') && c!='\n');
    x=c-'0';
    while(1){
        while((c=getchar())>='0' && c<='9')x=(x<<3)+(x<<1)+c-'0';
        New[i]|=(1<<(x-1));
        while(!((c=getchar())>='0' && c<='9') && c!='\n');
        if(c=='\n' || c==EOF)return;
        x=c-'0';
    }
}
int main(){  
    int s,m,n,salary=0;
    scanf("%d%d%d",&s,&m,&n);
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)f[i][j]=1e9;  
    for(int i=1;i<=m;i++){  
        int tt;scanf("%d",&tt); 
        salary+=tt;
        Get1();
    }
    for(int i=1;i<=s;i++)
        if(cnt[i]>1)B|=(1<<(i-1));
    f[A][B]=salary;
    for(int i=1;i<=n;i++){  
        scanf("%d",&Cost[i]);  
        Get2(i);
    }
    int ss=(1<<s)-1;
    for(int k=1;k<=n;k++)
        for(int i=ss;i>=0;i--)
            for(int j=ss;j>=0;j--){
                if(f[i][j]==1e9)continue;
                int a=(i|New[k]),b=((New[k]&i)|j);
                f[a][b]=min(f[a][b],f[i][j]+Cost[k]);
            }
    printf("%d",f[ss][ss]);return 0;
}

这道题还有一个坑点就是输入,行末竟然有空格……

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/80395075