【题解】 [SCOI2012]滑雪
目錄
- 題目
- 題目描述
- 輸入格式
- 輸出格式
- 輸入輸出樣例
- 輸入 #1
- 輸出 #1復制
- 說明/提示
- 【數據范圍】
- 題解
- 整理題目
- 思路
- 考慮順序
- 1.只能從高到低
- 2.到的景點最多、總距離最短
- 坑點
- 最后就是代碼
題目
題目描述
a180285 非常喜歡滑雪。他來到一座雪山,這里分布著 mmm 條供滑行的軌道和 nnn 個軌道之間的交點(同時也是景點),而且每個景點都有一編號 i(1≤i≤n)i\space (1 \le i \le n)i?(1≤i≤n)和一高度hih_ihi?。
a180285 能從景點 iii 滑到景點 jjj 當且僅當存在一條 iii 和 jjj 之間的邊,且 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 20001≤n≤2000;
對于 100%100\%100% 的數據,1≤n≤1051 \le n \le 10^51≤n≤105
對于所有的數據,保證 1≤m≤1061 \le m \le 10^61≤m≤106 , 1≤hi≤1091 \le h_i \le 10^91≤hi?≤109,1≤ki≤1091 \le k_i \le 10^91≤ki?≤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; }最后就是計算最小生成樹了,直接用模板即可,代碼就不單獨放了,免得又被你們說是湊字數
坑點
代碼打好了,愉快的交上去,錯了!什么鬼,是我寫錯了?請注意,這道題坑點有億點點多
最后就是代碼
我知道你們只喜歡這個
#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]滑雪的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java滑雪,AcWing 901. 滑
- 下一篇: 滑雪教程-新手必看(上)