JZOJ 5477. 【NOIP2017提高组正式赛】宝藏
Description
參與考古挖掘的小明得到了一份藏寶圖,藏寶圖上標出了 n 個深埋在地下的寶藏屋,也給出了這 n 個寶藏屋之間可供開發的 m 條道路和它們的長度。小明決心親自前往挖掘所有寶藏屋中的寶藏。但是,每個寶藏屋距離地面都很遠,也就是說,從地面打通一條到某個寶藏屋的道路是很困難的,而開發寶藏屋之間的道路則相對容易很多。小明的決心感動了考古挖掘的贊助商,贊助商決定免費贊助他打通一條從地面到某個寶藏屋的通道,通往哪個寶藏屋則由小明來決定。在此基礎上,小明還需要考慮如何開鑿寶藏屋之間的道路。已經開鑿出的道路可以任意通行不消耗代價。每開鑿出一條新道路,小明就會與考古隊一起挖掘出由該條道路所能到達的寶藏屋的寶藏。另外,小明不想開發無用道路,即兩個已經被挖掘過的寶藏屋之間的道路無需再開發。新開發一條道路的代價是:這條道路的長度 × 從贊助商幫你打通的寶藏屋到這條道路起點的寶藏屋所經過的寶藏屋的數量(包括贊助商幫你打通的寶藏屋和這條道路起點的寶藏屋)。請你編寫程序為小明選定由贊助商打通的寶藏屋和之后開鑿的道路,使得工程總代價最小,并輸出這個最小值。Input
第一行兩個用空格分離的正整數 n 和 m,代表寶藏屋的個數和道路數。
接下來 m 行,每行三個用空格分離的正整數,分別是由一條道路連接的兩個寶藏屋的編號(編號為 1~n),和這條道路的長度 v。
Output
輸出共一行,一個正整數,表示最小的總代價。
Sample Input
【輸入樣例 1】
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1
【樣例輸入 2】
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 2
Sample Output
【輸出樣例 1】
4
【輸入輸出樣例 1 說明】
小明選定讓贊助商打通了 1 號寶藏屋。小明開發了道路 1->2,挖掘了 2 號寶藏。開發了道路 1->4,挖掘了 4 號寶藏。還開發了道路 4->3,挖掘了 3 號寶藏。工程總代價為:1 × 1 + 1 × 1 + 1 × 2 = 4
(1->2) (1->4) (4->3)
【樣例輸出 2】
5
【輸入輸出樣例 2 說明】
小明選定讓贊助商打通了 1 號寶藏屋。小明開發了道路 1->2,挖掘了 2 號寶藏。開發了道路 1->3,挖掘了 3 號寶藏。還開發了道路 1->4,挖掘了 4 號寶藏。工程總代價為:1 × 1 + 3 × 1 + 1 × 1 = 5
(1->2) (1->3) (1->4)
Data Constraint
對于 20%的數據:
保證輸入是一棵樹,1≤n≤8,v≤5000 且所有的 v 都相等。
對于 40%的數據:
1≤n≤8,0≤m≤1000,v≤5000 且所有的 v 都相等。
對于 70%的數據:
1≤n≤8,0≤m≤1000,v≤ 5000
對于 100%的數據:
1≤n≤12,0≤m≤1000,v≤ 500000
Solution
這道題有兩個解法:搜索剪枝(再加估值函數等神奇優化),狀壓DP。
速度似乎搜索更快,但狀壓DP的時間復雜度更穩定。
看到 n≤12 ,自然就想到狀壓DP。
設 F[i][s] 表示最大深度為 i 、當前選了的點的集合為 s 的最小總代價。
再設 c[s][s1] 表示當前選了集合 s ,用這個集合連出去,直到選完補集 s1 的所選的邊的最小值。
由于直接求不好求,而且 n 奇小,可以盡情地預處理。
設 h[s][i] 表示當前選了集合為 s 的點,要連接 i 號點的邊的最小值。
那么容易處理出 h 數組,則就可以簡單地處理出 c 數組了。
為了更快地枚舉集合 s 的子集,我們還可以設一個 g[s][i] 表示其第 i 個補集。
同樣的,g 也能快速地預處理出來。那么我們就可以愉快地DP了。
由于深度小的更優解會在之前就計算出來,這樣做的正確性顯然。
注意處理 c 數組時,若兩集合不能連接(其中一個元素不能連接即說明整體連不了),
要將值賦為最大值,不然會將答案算小。
時間復雜度 O(22n?n) ,輕松過掉。
Code
#include<cstdio> #include<cstring> #include<cctype> using namespace std; const int N=13,M=(1<<N-1)+5,inf=1e9; int a[N][N],p[N],f[N][M],g[M][M],h[M][N],c[M][M]; inline int read() {int X=0,w=0; char ch=0;while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X; } inline int min(int x,int y) {return x<y?x:y; } int main() {int n=read(),m=read();memset(a,60,sizeof(a));for(int i=1;i<=m;i++){int x=read(),y=read(),z=read();if(z<a[x][y]) a[y][x]=a[x][y]=z;}for(int i=p[0]=1;i<N;i++) p[i]=p[i-1]<<1;for(int i=0;i<p[n];i++)for(int j=1;j<p[n];j++)if(!(i&j)) g[i][++g[i][0]]=j;memset(h,60,sizeof(h));for(int i=1;i<p[n];i++)for(int j=1;j<=n;j++)if(!(i&p[j-1]))for(int k=1;k<=n;k++)if(i&p[k-1]) h[i][j]=min(h[i][j],a[k][j]);for(int i=1;i<p[n];i++)for(int j=1;j<=g[i][0];j++)for(int k=1;k<=n;k++)if(p[k-1]&g[i][j])if(h[i][k]<inf) c[i][g[i][j]]+=h[i][k]; else{c[i][g[i][j]]=inf;break;}memset(f,60,sizeof(f));for(int i=1;i<=n;i++) f[0][p[i-1]]=0;for(int i=0;i<n;i++)for(int j=0;j<p[n];j++)for(int k=1;k<=g[j][0];k++)if(f[i][j]<inf && c[j][g[j][k]]<inf)f[i+1][j|g[j][k]]=min(f[i+1][j|g[j][k]],f[i][j]+c[j][g[j][k]]*(i+1));int ans=inf;for(int i=0;i<=n;i++) ans=min(ans,f[i][p[n]-1]);printf("%d",ans);return 0; }總結
以上是生活随笔為你收集整理的JZOJ 5477. 【NOIP2017提高组正式赛】宝藏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5475. 【NOIP2017
- 下一篇: JZOJ 5478. 【NOIP2017