【題解】LuoGu3959/noip2017:寶藏
阿新 • • 發佈:2018-12-13
狀壓dp
表示當前在j節點,j到起點距離為i,從j開始挖的點的集合為s的最小代價 所以我們要先保證j不屬於s,再枚舉出一個屬於s的集合s2,在s2裡枚舉出一個屬於s2的點k,S1記為s2-{k},S2記為
狀態轉移方程:
最終列舉起點求得答案
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.