1. 程式人生 > >UVA10817 Headmaster's Headache (狀壓DP)

UVA10817 Headmaster's Headache (狀壓DP)

題意:

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;
}

看了看紫書的思路,一遍過還是很開心的。