題目網址: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;
}
看了看紫書的思路,一遍過還是很開心的。