看到这道题,发现不会最小生成树,遂昨晚熬夜补了prim算法,今天来写这道题。
看到时间胶囊能回溯,因此确实可以想得到是最小生成树,其实就是最小生成树后求路径长度和最小的问题。这道唯一特别一点的地方就是只能从高处到低处,并且要保证能去的地方最多,所以贪心策略需要加一条:以边终点高的优先选择,保证高度高(能去地方最多)的情况下选择最短路径。
之后,就是prim算法。这里我选择了堆优化。以1为起点,把和1相关的路径全部push进堆里,之后按照贪心策略取出,取到没有访问过的点就如法炮制丢路劲进来。
90分答案:
#include <iostream> #include <map> #include <vector> #include <climits> #include <algorithm> #include <random> #include <cstring> #include <cstdio> #include <queue> #include <map> #include <set> #include <bitset> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define rep(i, a, n) for(register int i = a; i <= n; ++ i) #define per(i, a, n) for(register int i = n; i >= a; -- i) #define ONLINE_JUDGE using namespace std; typedef long long ll; const int mod=1e9+7; template<typename T>void write(T x) { if(x<0) { putchar('-'); x=-x; } if(x>9) { write(x/10); } putchar(x%10+'0'); } template<typename T> void read(T &x) { x = 0;char ch = getchar();ll f = 1; while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();} while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f; } ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} ll lcm(ll a,ll b){return a/gcd(a,b)*b;}; ll ksm(ll a,ll n){//看是否要mod ll ans=1; while(n){ if(n&1) ans=(ans*a)%mod; a=a*a%mod; n>>=1; } return ans%mod; } const int maxn=1e5+10; int n,m; int h[maxn]; struct Edge{ int v,k; bool operator<(const Edge&b)const{ return h[v]==h[b.v]?k>b.k:h[v]<h[b.v]; } }; vector<Edge> e[maxn]; void add(int u,int v,int k){ if(h[u]<h[v]) swap(u,v); Edge tmp={v,k}; e[u].push_back(tmp); if(h[u]==h[v]){ e[v].push_back(Edge{u,k}); } } priority_queue<Edge>que; int vis[maxn]; int cnt; ll prim(){ ll res=0; int now=1; vis[1]=1; for(int i=0;i<e[now].size();i++){ if(vis[e[now][i].v]==0)que.push(e[now][i]); } cnt++; while(!que.empty()){ Edge a=que.top(); que.pop(); if(vis[a.v]==0){ cnt++; now=a.v; res+=a.k; vis[a.v]=1; for(int i=0;i<e[now].size();i++){ que.push(e[now][i]); } } } return res; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif //=========================================================== read(n),read(m); rep(i,1,n) read(h[i]); rep(i,1,m){ int u,v,k; read(u),read(v),read(k); add(u,v,k); } ll ans=prim(); write(cnt),putchar(' '); write(ans); //=========================================================== return 0; }
之后再优化一下:如果路径的终点被访问过就不push进来,就过了。
AC代码
#include <iostream> #include <map> #include <vector> #include <climits> #include <algorithm> #include <random> #include <cstring> #include <cstdio> #include <queue> #include <map> #include <set> #include <bitset> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define rep(i, a, n) for(register int i = a; i <= n; ++ i) #define per(i, a, n) for(register int i = n; i >= a; -- i) #define ONLINE_JUDGE using namespace std; typedef long long ll; const int mod=1e9+7; template<typename T>void write(T x) { if(x<0) { putchar('-'); x=-x; } if(x>9) { write(x/10); } putchar(x%10+'0'); } template<typename T> void read(T &x) { x = 0;char ch = getchar();ll f = 1; while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();} while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f; } ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} ll lcm(ll a,ll b){return a/gcd(a,b)*b;}; ll ksm(ll a,ll n){//看是否要mod ll ans=1; while(n){ if(n&1) ans=(ans*a)%mod; a=a*a%mod; n>>=1; } return ans%mod; } const int maxn=1e5+10; int n,m; int h[maxn]; struct Edge{ int v,k; bool operator<(const Edge&b)const{ return h[v]==h[b.v]?k>b.k:h[v]<h[b.v]; } }; vector<Edge> e[maxn]; void add(int u,int v,int k){ if(h[u]<h[v]) swap(u,v); Edge tmp={v,k}; e[u].push_back(tmp); if(h[u]==h[v]){ e[v].push_back(Edge{u,k}); } } priority_queue<Edge>que; int vis[maxn]; int cnt; inline ll prim(){ ll res=0; int now=1; vis[1]=1; for(int i=0;i<e[now].size();i++){ if(vis[e[now][i].v]==0)que.push(e[now][i]); } cnt++; while(!que.empty()){ Edge a=que.top(); que.pop(); if(vis[a.v]==0){ cnt++; now=a.v; res+=a.k; vis[a.v]=1; for(int i=0;i<e[now].size();i++){ if(vis[e[now][i].v]==0) que.push(e[now][i]); } } } return res; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif //=========================================================== read(n),read(m); rep(i,1,n) read(h[i]); rep(i,1,m){ int u,v,k; read(u),read(v),read(k); add(u,v,k); } ll ans=prim(); write(cnt),putchar(' '); write(ans); //=========================================================== return 0; }