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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【题解】 [SCOI2012]滑雪

發布時間:2023/12/8 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【题解】 [SCOI2012]滑雪 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 題目
    • 題目描述
    • 輸入格式
    • 輸出格式
    • 輸入輸出樣例
      • 輸入 #1
      • 輸出 #1復制
    • 說明/提示
      • 【數據范圍】
  • 題解
    • 整理題目
    • 思路
      • 考慮順序
      • 1.只能從高到低
      • 2.到的景點最多、總距離最短
    • 坑點
    • 最后就是代碼

題目

題目描述

a180285 非常喜歡滑雪。他來到一座雪山,這里分布著 mmm 條供滑行的軌道和 nnn 個軌道之間的交點(同時也是景點),而且每個景點都有一編號 i(1≤i≤n)i\space (1 \le i \le n)i?(1in)和一高度hih_ihi?

a180285 能從景點 iii 滑到景點 jjj 當且僅當存在一條 iiijjj 之間的邊,且 iii 的高度不小于 jjj。與其他滑雪愛好者不同,a180285 喜歡用最短的滑行路徑去訪問盡量多的景點。如果僅僅訪問一條路徑上的景點,他會覺得數量太少。

于是 a180285 拿出了他隨身攜帶的時間膠囊。這是一種很神奇的藥物,吃下之后可以立即回到上個經過的景點(不用移動也不被認為是 a180285 滑行的距離)。

請注意,這種神奇的藥物是可以連續食用的,即能夠回到較長時間之前到過的景點(比如上上個經過的景點和上上上個經過的景點)。 現在,a180285站在 111 號景點望著山下的目標,心潮澎湃。他十分想知道在不考慮時間膠囊消耗的情況下,以最短滑行距離滑到盡量多的景點的方案(即滿足經過景點數最大的前提下使得滑行總距離最小)。你能幫他求出最短距離和景點數嗎?

輸入格式

輸入的第一行是兩個整數 n,mn,mn,m。 接下來一行有 nnn 個整數 hih_ihi? ,分別表示每個景點的高度。
接下來 mmm 行,表示各個景點之間軌道分布的情況。每行三個整數 u,v,ku,v,ku,v,k,表示編號為 uuu 的景點和編號為 vvv 的景點之間有一條長度為 kkk 的軌道。

輸出格式

輸出一行,表示 a180285 最多能到達多少個景點,以及此時最短的滑行距離總和。

輸入輸出樣例

輸入 #1

3 3 3 2 1 1 2 1 2 3 1 1 3 10

輸出 #1復制

3 2

說明/提示

【數據范圍】

對于 30%30\%30% 的數據,1≤n≤20001 \le n \le 20001n2000
對于 100%100\%100% 的數據,1≤n≤1051 \le n \le 10^51n105
對于所有的數據,保證 1≤m≤1061 \le m \le 10^61m106 , 1≤hi≤1091 \le h_i \le 10^91hi?1091≤ki≤1091 \le k_i \le 10^91ki?109

題解

整理題目

可以發現,整個景區可以抽象為一張圖(明顯廢話),景點的高度為點權,景點間的距離為邊權,所以這個長的像坨shit的題面就被我們翻譯成了一句話:在只能從點權大的點到點權小的點(可以相等)的情況下,從1點出發建立一棵盡可能有更多點的最小生成樹

思路

考慮順序

在這種多條件題目中,我們有一個優先考慮順序(反正我是這樣的啦),即先考慮是否可行,再考慮最優,比如這道題,我們就先考慮一些硬性的規定:只能從高滑到低,再考慮景點多、距離小這樣的條件

1.只能從高到低

如果給你一張無向圖,它已經滿足每個景點都可以從1號景點按要求到達,那是不是這個點就解決了?那必須的呀,我們想要的就是這樣一張圖呀!所以現在考慮造出這樣一張圖。想想我們知道什么,只有每個景點的高度及每兩個景點的連通性(距離我們暫時不需要),這就夠了!對于每一條邊,我們規定它的方向為從高的連向低的,讓后從1號景點開始跑一遍BFS/DFS,所有能跑到的點以及經過的邊組成一張新圖,這張新圖就是前面說的滿足要求的圖了

代碼:

//tmp為記錄能到的點數(題目要問) //vis為記錄是否以及遍歷過這個點,防止不斷遞歸 //g為原圖 //g_new為新圖 void dfs(int i){if(vis[i])return;tmp++;vis[i]=true;for(int j=0;j<g[i].size();j++){g_new[++cnt].u=i;g_new[cnt].v=g[i][j].u;g_new[cnt].w=g[i][j].w;dfs(g[i][j].u);}return; }

2.到的景點最多、總距離最短

要讓到的景點最多,肯定是能到的點都要到,也就是說,我們在剛才DFS遍歷的時候就應該存儲一下能到的點的個數,直接輸出。

這就完了?開始考慮總距離最短?錯!能到的點一定可以到沒問題,但是不能光考慮總距離,如果光考慮總距離最小,就有可能出現反著爬坡的情況(因為最小生成樹是不會考慮邊的方向的),那怎么辦?

在對邊排序的時候,按終點的高度從大到小進行排序,這樣就能從高到低下來了

還有個總距離最短的條件,這個簡單,在高度的基礎上,以邊權為第二關鍵字,從小到大排即可

代碼

bool cmp(edge x,edge y){if(hi[x.v]!=hi[y.v]){return hi[x.v]>hi[y.v];}return x.w<y.w; }

最后就是計算最小生成樹了,直接用模板即可,代碼就不單獨放了,免得又被你們說是湊字數

坑點

代碼打好了,愉快的交上去,錯了!什么鬼,是我寫錯了?請注意,這道題坑點有億點點多

  • 每條邊的最大值是10910^9109,而最多有10610^6106條邊,嗯。。。爆int穩穩的,該怎么改我不說了吧,高精啊!,long long啊!
  • 相同高度是可以到達的,所以在建立原圖的時候,如果兩的點高度相同,需要建立兩條邊互相連接,這樣后面DFS/BFS遍歷的時候才不會錯
  • 都建立了兩條邊,邊數量的最大值不改改?肯定要兩倍快樂呀,不然RE穩穩的
  • 最后就是代碼

    我知道你們只喜歡這個

    #include<cstdio> #include<iostream> #include<vector> #include<algorithm> using namespace std; const int MAXN=100005; const int MAXM=2000005; struct node{int u;int w;node(){}node(int U,int W){u=U;w=W;} }; struct edge{int u,v;int w; }g_new[MAXM]; int hi[MAXN]; vector<node> g[MAXN]; bool vis[MAXN]; int cnt=0; int tmp=0; void dfs(int i){if(vis[i])return;tmp++;vis[i]=true;for(int j=0;j<g[i].size();j++){g_new[++cnt].u=i;g_new[cnt].v=g[i][j].u;g_new[cnt].w=g[i][j].w;dfs(g[i][j].u);}return; } int fa[MAXN]; int get(int x){if(fa[x]==x){return x;}return fa[x]=get(fa[x]); } bool cmp(edge x,edge y){if(hi[x.v]!=hi[y.v]){return hi[x.v]>hi[y.v];}return x.w<y.w; } long long kruskal(int m){long long sum=0;sort(g_new+1,g_new+1+m,cmp); // for(int i=1;i<=m;i++){ // printf("%d %d %d\n",g_new[i].u,g_new[i].v,g_new[i].w); // }for(int i=1;i<=m;i++){int fu=get(g_new[i].u);int fv=get(g_new[i].v);if(fu!=fv){fa[fv]=fu;sum+=g_new[i].w;}}return sum; } int main(){int n,m;scanf("%d %d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&hi[i]);}int u,v,w;for(int i=1;i<=m;i++){scanf("%d %d %d",&u,&v,&w);if(hi[u]<hi[v]){swap(u,v);}g[u].push_back(node(v,w));if(hi[u]==hi[v]){g[v].push_back(node(u,w));}}dfs(1);for(int i=1;i<=n;i++){fa[i]=i;}printf("%d %lld",tmp,kruskal(cnt)); return 0; }

    如果我的題解對你有幫助,幫我把下面那個慘白的豎著大拇指的手點紅唄
    👇

    總結

    以上是生活随笔為你收集整理的【题解】 [SCOI2012]滑雪的全部內容,希望文章能夠幫你解決所遇到的問題。

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