题目网址: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;
}
看了看紫书的思路,一遍过还是很开心的。