1. 程式人生 > >【題解】LuoGu3959/noip2017:寶藏

【題解】LuoGu3959/noip2017:寶藏

原題傳送門

狀壓dp

dp[i][j][s]dp[i][j][s]表示當前在j節點,j到起點距離為i,從j開始挖的點的集合為s的最小代價 所以我們要先保證j不屬於s,再枚舉出一個屬於s的集合s2,在s2裡枚舉出一個屬於s2的點k,S1記為s2-{k},S2記為ss2s-s2

狀態轉移方程:dp[i][j][s]=min(dp[i+1][k][S1]+dp[i][j][S2]+w[j][k](i+1))dp[i][j][s]=min(dp[i+1][k][S1]+dp[i][j][S2]+w[j][k]*(i+1))

][k][S1]+dp[i][j][S2]+w[j][k](i+1))

最終列舉起點求得答案

Code:

uses math;
var
    dp:array[0..20,0..20,0..10000] of longint;
    w:array[0..100,0..100] of longint;
    pow,p:array[0..10000] of longint;
    n,m,x,y,z,i,j,k,s1,s2,s3:longint;
    ans:int64;

begin
    readln(n,m);
    fillchar(w,sizeof(w),$7f);
    fillchar(dp,sizeof(dp),$7f);
    for i := 1 to m do
    begin
        readln(x,y,z);
        dec(x); dec(y);
        w[x][y] := min(w[x][y], z);
        w[y][x] := min(w[y][x], z);
    end;
    pow[0] := 1;
    for i := 1 to n do pow[i] := pow[i - 1] << 1;
    for i := 0 to n - 1 do p[pow[i]] := i; //p[2^i]=i,預處理
    for i := 0 to n - 1 do dp[n - 1][i][0] := 0;
    for i := n - 2 downto 0 do
        for j := 0 to n - 1 do
        begin
            dp[i][j][0] := 0;
            for s1 := 1 to pow[n] - 1 do
                if s1 and pow[j] = 0 then
                begin
                    s2 := s1;
                    while s2 > 0 do
                    begin
                        if dp[i][j][s1 and not s2] < dp[i][j][s1] then
                        begin
                            s3 := s2;
                            while s3 > 0 do
                            begin
                                x := s3 and -s3;
                                //x列舉s3包括的點
                                y := p[x];
                                if w[j][y] < 2000000000 then
                                    dp[i][j][s1] := min(dp[i][j][s1],
                                        (i + 1) * w[j][y] + dp[i + 1][y][s2 and not x] + dp[i][j][s1 and not s2]);
                                dec(s3, x);
                            end;
                        end;
                        s2 := (s2 - 1) and s1; 
                        //s2列舉s1的子集
                    end;
                end;
        end;
    ans := maxlongint;
    for i := 0 to n - 1 do ans := min(ans, dp[0][i][(pow[n] - 1) and not pow[i]]);
    writeln(ans);
end.