UVA10817 Headmaster's Headache (状压DP)

题目网址:https://vjudge.net/problem/UVA-10817

题意:

S门课程,M个教师,N个待聘教师。每个教师都有工资数和能够教授课程种类,要求在M个教师全部选择的基础上,再选择性的雇佣一部分待聘教师,在保证S门课程都至少有2人教授的前提下,付出的工资最少。s<8

思路:

 我们通过课程信息来确定最终结果,用三个状态s0,s1,s2的二进制位来代表课程当前的状态,s0中的对应二进制位为1表示该课程没有人教授,s1为1表示该课程只有一个人教授,s2为1表示该课程至少两个人教授。

我们通过枚举每位待聘教师来确定最终的答案,很明显转移方程为 dpi   =   min(dp(i+1,news1,news2)+val[ i ] ,dp( i+1, s1,s2) )

即雇佣或者不雇佣该教师的最小值。

因为三个二进制状态的关系,所以我们只需要保留s1和s2就可以很轻易的算出s0 。s0= (s1|s2) ^(全为1);

接下来记忆化搜索一下就行。 

代码如下:

 

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <map>
#include <cstring>

using namespace std;
const int maxn = 150;
const int maxs = (1<<8)+5;
const int inf = 0x3f3f3f3f;
int dp[maxn][maxs][maxs];
int S,M,N;
int val[maxn];
int s[maxn];
int all;
int DP(int t,int s1,int s2)
{
    if(s2==all)
        return 0;
    if(t==N)
    {
        if(s2==all)
            return 0;
        return inf;
    }
    int s0 = ((s1|s2)^all);
    int& res = dp[t][s1][s2];
    if(res!=-1)
        return res;
    int nows = s[t];
    int news1=s1,news2=s2;
    for(int x=0; x<S; x++)
    {
        if(!(nows&(1<<x)))
            continue;
        if(s0&(1<<x))
        {
            news1 = (news1|(1<<x));
        }
        else if(news1&(1<<x))
        {
            news1 = (news1^(1<<x));
            news2 = (news2|(1<<x));
        }
    }
    //cout<<" t="<<t<<" s1="<<s1<<" s2="<<s2<<" new1= "<<news1<<" new2="<<news2<<endl;
    res = min(DP(t+1,s1,s2),DP(t+1,news1,news2)+val[t]);
    return res;
}
char cnt[1000];
void getm(int &s1,int& s2,int &res)
{
    int x=0;
    int okv = 0;
    int len = strlen(cnt);
    int s0 = ((s1|s2)^all);
    for(int i=0; i<len; i++)
    {
        if(cnt[i]==' ')
        {
            if(!okv)
            {
               res+= x;
                x = 0;
            }
            else
            {
                x--;
                if(s0&(1<<x))
                {
                    s0 = (s0^(1<<x));
                    s1 = (s1|(1<<x));
                }
                else if(s1&(1<<x))
                {
                    s1 = (s1^(1<<x));
                    s2 = (s2|(1<<x));
                }
                x = 0;
            }
            okv = 1;
        }
        else
            x = x*10+ cnt[i]-'0';
    }
}
void getn(int t)
{
    int x=0;
    int okv = 0;
    int len = strlen(cnt);
    int nows = 0;
    for(int i=0; i<len; i++)
    {
        if(cnt[i]==' ')
        {
            if(!okv)
            {
                val[t] = x;
                x = 0;
            }
            else
            {
                x--;
                nows|=(1<<x);
                x = 0;
            }
            okv = 1;
        }
        else
            x = x*10+ cnt[i]-'0';
    }
    // cout<<t<<" nows"<<nows<<endl;
    s[t] = nows;
}
int main()
{
     while(scanf("%d%d%d",&S,&M,&N) && S!=0)
    {
        getchar();
        int len;
        all = (1<<(S))-1;
        int s1 = 0;
        int s2 = 0;
        int ans = 0;
        for(int i=0; i<M; i++)
        {
            gets(cnt);
            len = strlen(cnt);
            cnt[len]=' ';
            cnt[++len]='\0';
            getm(s1,s2,ans);
        }
        for(int i=0; i<N; i++)
        {
            gets(cnt);
            len = strlen(cnt);
            cnt[len]=' ';
            cnt[++len]='\0';
            getn(i);
        }
//        cout<<s1<<" "<<s2<<" all="<<all<<endl;
        memset(dp,-1,sizeof(dp));
        ans+=DP(0,s1,s2);
        printf("%d\n",ans);
    }
    return 0;
}

看了看紫书的思路,一遍过还是很开心的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
创建校长信息表`HeadMaster`的SQL语句如下: ``` CREATE TABLE HeadMaster ( HeadID CHAR(4) NOT NULL, Head_Name CHAR(10) NOT NULL, S_ID CHAR(4) NOT NULL, App_Date DATETIME, CONSTRAINT PK_HeadMaster PRIMARY KEY (HeadID), CONSTRAINT FK_HeadMaster_S_ID FOREIGN KEY (S_ID) REFERENCES School(S_ID) ); ``` 其中,`HeadID`、`Head_Name`、`S_ID`、`App_Date`分别是校长信息表`HeadMaster`的列名,对应的中文列名分别是校长编号、姓名、学校编号、任职日期。数据类型和长度分别如题所述。`App_Date`列为校长的任职日期,使用了`Datetime`类型。 主键约束使用`PK_HeadMaster`名称进行命名,外键约束使用`FK_HeadMaster_S_ID`进行命名,对应`S_ID`列的外键关系。约束必须引用`School`表的`S_ID`列。 以上SQL语句创建了一个校长信息表`HeadMaster`,其中主键为`HeadID`列,`S_ID`列为`School`表的外键。 创建学校信息表`School`的SQL语句如下: ``` CREATE TABLE School ( S_ID CHAR(4) NOT NULL, S_Name VARCHAR(20) NOT NULL, s_add CHAR(20), CONSTRAINT PK_School PRIMARY KEY (S_ID) ); ``` 其中,`S_ID`、`S_Name`、`s_add`分别是学校信息表`School`的列名,对应的中文列名分别是学校编号、学校名称、学校地址。数据类型和长度分别如题所述。 主键约束使用`PK_School`名称进行命名。 以上SQL语句创建了一个学校信息表`School`,其中主键为`S_ID`列。 创建教师类型表`TeacherType`的SQL语句如下: ``` CREATE TABLE TeacherType ( TypeID CHAR(4) NOT NULL, TypeName CHAR(20) NOT NULL, CONSTRAINT PK_TeacherType PRIMARY KEY (TypeID) ); ``` 其中,`TypeID`、`TypeName`分别是教师类型表`TeacherType`的列名,对应的中文列名分别是类型编号、类型名称。数据类型和长度分别如题所述。 主键约束使用`PK_TeacherType`名称进行命名。 以上SQL语句创建了一个教师类型表`TeacherType`,其中主键为`TypeID`列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值