Solution [SCOI2012]滑雪
题目大意:给你一个图,每个点有一个高度,你只能从一个点走向高度不大于它的点,求在可以走的点最多的限制下的最小权值和
最小生成树,贪心
分析:如果不考虑高度限制,这题实质上是一个最小生成树,关键在于考虑上高度限制
既然要使得走到的点最多,显然我们优先从一个点走向它可以走的点集里面高度最高的点,这样可以使得答案最优.然后在这个基础上使得权值之和最小
可以用类似(Prim)算法求解,朴素(Prim)从可以扩展的点集(V)里面选择离生成树最近的点扩展,这里选择高度最大的点扩展,如果高度一样选择离生成树最近的即可
貌似还可以用(Kruskal?),不过感觉不是很直观的样子(主要是(TCL))
代码:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100,maxm = 2e6 + 100;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
struct Edge{
int from,to,dist;
}Edges[maxm];
int head[maxn],nxt[maxm],n,m;
inline void addedge(int from,int to,int dist){
static int tot;
Edges[++tot] = Edge{from,to,dist};
nxt[tot] = head[from];
head[from] = tot;
}
int high[maxn];
ll ans1,ans2;
int dis[maxn],vis[maxn];
struct Point{
int u,high,dis;
bool operator < (const Point &rhs)const{
return (high == rhs.high) ? (dis > rhs.dis) : (high < rhs.high);
}
};
priority_queue<Point> Q;
int main(){
n = read(),m = read();
for(int i = 1;i <= n;i++)high[i] = read();
for(int u,v,d,i = 1;i <= m;i++)
u = read(),v = read(),d = read(),addedge(u,v,d),addedge(v,u,d);
memset(dis,0x3f,sizeof(dis));
dis[1] = 0;Q.push(Point{1,high[1],dis[1]});
while(!Q.empty()){
Point p = Q.top();Q.pop();
int u = p.u;
if(vis[u])continue;
vis[u] = 1;
ans1++;
ans2 += dis[u];
for(int i = head[u];i;i = nxt[i]){
int v = Edges[i].to;
if(vis[v])continue;
if(high[u] < high[v])continue;
if(Edges[i].dist < dis[v]){
dis[v] = Edges[i].dist;
Q.push(Point{v,high[v],dis[v]});
}
}
}
printf("%lld %lld
",ans1,ans2);
return 0;
}