日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P3959 [NOIP2017 提高组] 宝藏

發布時間:2023/12/3 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P3959 [NOIP2017 提高组] 宝藏 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

P3959 [NOIP2017 提高組] 寶藏

題意:

額題意不好說,就是n個點m個邊,選定一個點為根節點,構造一個最小生成樹,邊的權值為該該邊起點到根節點之間的點的數量K(不含根節點) * 道路長度
1<=n<=12
0<=m<=1e3
v<=5e5

題解:

參考題解
這數據范圍?暴力暴力暴力
不,我們用狀壓dp來做
我們設dp[i][j]表示用到第i個元素,當前連接狀態為j的花費的最小值
這個式子沒辦法直接轉移,因為每個邊的花費是不一樣的,即k是不一樣的,我們可以重新設計一個狀態,我們將k值理解為距離初始化點的層數,如圖

被涂藍色的就是根節點,k就是劃分的層數
這樣我們設dp[i][j]表示到第i層,總共取了的點的狀態為j
轉移為:
dp[i][j] = min(dp[i-1][k]+trans[k][j] * (i-1))
trans[k][j] * (i-1)就是題目說的距離 * K(題目中說的k)
k是j的子集,即有可能轉移到j的狀態
trans[k][j]表示從狀態k轉移到狀態j的最小花費路徑
這個子集意思就是:sub就是S的子集
這個子集怎么求??
直接求2^n必然不行,會T,有小技巧
由公式:

for (int sub = S; sub; sub = (sub - 1) & S) {// sub 為 S 的子集 }

證明過程

最終答案就是:min(dp[i][2n-1])
初始化:dp[1][2(i-1)] = 0 (i∈[1,n])
我感覺代碼很妙,思路也很妙,讓我想是真寫不出來
我詳細說trans如何求:
現在i是當前狀態,j是i的子狀態,我們現在要狀態轉移從j–>i
temp=i ^ j,即要轉移的點(因為 ^ 為不同為1)
然后我們開始枚舉temp中存在的點k(從高位往低位),然后求從k到j的最短距離tmin,把tmin加入到trans[j][i]中

代碼:

#include<iostream> #include<cstdio> #include<cstring> using namespace std; long long read() {long long x=0,f=1; char c=getchar();while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f; } const int N=12+2; const int M=1<<N; int n,m,dis[N][N],trans[M][M],POW[N]; long long f[N][M]; int main() {n=read(),m=read();memset(dis,0x3f,sizeof dis);for(int i=1;i<=m;i++){int s=read(),t=read(),v=read();if(dis[s][t]>v)dis[s][t]=dis[t][s]=v;}for(int i=0;i<(1<<n);i++)for(int j=i;j!=0;j=(j-1)&i){bool OK=true;int temp=i^j;//狀態i與狀態j的不同之處,狀態轉移為j->i for(int k=n-1;k>=0;k--){if((temp>>k)&1)//說明點k是轉移中增加的點,即 j沒有,i有 {int tmin=0x3f3f3f3f;for(int L=1;L<=n;L++)if(1&(j>>(L-1)))//如果狀態j包含此點 ,求出L到k+1的最短距離//if(((1<<(L-1))&j)==(1<<(L-1))) tmin=min(tmin,dis[L][k+1]);if(tmin==0x3f3f3f3f)//如果此路無法走通 {OK=false;break;}trans[j][i]+=tmin;/*相當于把j到i這段路拆分成了好幾份 */ temp-=(1<<k);}}if(OK==false)trans[j][i]=0x3f3f3f3f;}memset(f,0x3f,sizeof f);for(int i=1;i<=n;i++)f[1][(1<<(i-1))]=0;for(int i=2;i<=n;i++)for(int j=0;j<(1<<n);j++)for(int k=j;k!=0;k=(k-1)&j)//k為j的子狀態,也就是k是j的子集 if(trans[k][j]!=0x3f3f3f3f)//說明可以從狀態k到j f[i][j]=min(f[i][j],f[i-1][k]+(i-1)*trans[k][j]);long long ans=0x3f3f3f3f3f3f3f3fll;for(int i=1;i<=n;i++)ans=min(ans,f[i][(1<<n)-1]);printf("%lld",ans);return 0; }

總結

以上是生活随笔為你收集整理的P3959 [NOIP2017 提高组] 宝藏的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。