HDU 4281 Judges' response 狀態壓縮 01揹包 MTSP

題意:比賽上,有N個選手提出了問題,解決每個選手的問題需要的時間是Ci。現在每個裁判至多能爲選手解答時間長爲M的問題。至少需要幾個裁判才能解決所有的選手的問題。

           同時,給出每個選手的位置座標xi,yi.希望所有裁判從起始點出發,解決完所有問題,再回到起始點,所走的距離和最小。

思路:其實這兩問的答案是基本沒有關係。唯一的關係,就是如果第一問沒有解的話,第二問也是沒有解的。所以,我們分別討論這兩個問題。

           對於第一問,我們注意到,N很小,這是指數複雜度的一個標誌。我們的目標是完成所有選手的問題,而這個可以利用狀態壓縮作爲最終的目標。

           因爲我們要求至少需要多少個裁判,所以要先預處理一個裁判,可以完成那些選手的問題。

           之後,就是狀態壓縮形式的01揹包了。

           對於第二問:如果問題簡化爲1個裁判的話,就是個旅行商問題,即用最短的距離訪問所有的點並返回。要是裁判多了的話,其實就是個多旅行商問題(MTSP)。

           對於MTSP問題,我們首先求出1個旅行商的TSP問題,這個可以用狀態壓縮的DP得到。

           然後就是把單個旅行商合成多個旅行商,其實這也類似第一問的狀態壓縮形式的01揹包。

注意:在TSP的問題中,要注意初始化,將起始的城市初始化爲0.

代碼如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
#include <cmath>
#include <vector>

using namespace std;

const int MAX = 17;

int x[MAX],y[MAX],dis[MAX][MAX];
int c[MAX],N,M;
int dp[1<<MAX],dp2[1<<MAX][20],va[1<<MAX],vec[1<<MAX],sz,all;

int judge(int s)
{
    int sum = 0;
    for(int i = 0; i < N; ++i)
        if((s>>i) & 1)
            sum += c[i];
    return va[s] = (sum <= M);
}

void pre_work()
{
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < i; ++j){
            double dx = x[i] - x[j];
            double dy = y[i] - y[j];
            dis[i][j] = dis[j][i] = ceil(sqrt(dx*dx+dy*dy));
        }
        dis[i][i] = 0;
    }
    sz = 0;
    all = (1<<N) - 1;
    for(int i = 0; i <= all; ++i)
        if(judge(i))
            vec[sz++] = i;
}

int solve1()
{
    memset(dp,0x3f,sizeof(dp));
    dp[0] = 0;
    for(int i = 0; i < sz; ++i){
        for(int j = all; j >= 0; --j){
            int s = j + vec[i];
            if(s != (j | vec[i])) continue;
            dp[s] = min(dp[s],dp[j]+1);
        }
    }
    return dp[all];
}

int solve2()
{
    memset(dp2,0x3f,sizeof(dp2));
    memset(dp,0x3f,sizeof(dp));
    dp2[1][0] = 0;
    for(int s = 0; s <= all; ++s) if(va[s])
        for(int i = 0; i < N; ++i) if((s>>i)&1){
            dp[s] = min(dp[s],dp2[s][i] + dis[i][0]);
            for(int j = 0; j < N; ++j) if(!((s>>j)&1))
                dp2[s|(1<<j)][j] = min(dp2[s|(1<<j)][j],dp2[s][i]+dis[i][j]);
        }

    for(int s = 0; s <= all; ++s) if(s&1)
        for(int j = (s-1) & s;j; j = (j-1) & s)
            dp[s] = min(dp[s],dp[j|1] + dp[(s-j) | 1]);
    return dp[all];
}

int main(void)
{
    //freopen("input.txt","r",stdin);
    while(scanf("%d%d",&N,&M) != EOF){
        for(int i = 0; i < N; ++i)
            scanf("%d%d",&x[i],&y[i]);
        for(int i = 0; i < N; ++i)
            scanf("%d",&c[i]);
        pre_work();
        int ans1 = solve1();
        if(ans1 == 0x3f3f3f3f)
            puts("-1 -1");
        else
            printf("%d %d\n",ans1,solve2());
    }
    return 0;
}

發佈了283 篇原創文章 · 獲贊 4 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章