题意:
就是给你一个n个点的树,n-1条边。刚开始每个点的权值都是0,然后一个点如果度<2那么可以直接从0变为2,不需要花费。还可以让一个点权为2的变成-1,然后让他的其中一个邻接点的权值+1,花费就是边权。
思考:
其实看到感觉说的很复杂,这种题就是一种什么模型,看懂就行了。就是度<=2的可以直接变,其余的点都需要从2个点来合成,所以刚开始我写了个dfs,但是发现也可以从父亲转移,所以加了个get但是这样肯定不对把,就像模拟一样。然后发现这不就是一个图吗,直接从叶子节点去跑也行呀,我想着就是直接更新,但是可能还不够贪心,所以要用优先队列,然后再去更新别人。这个时候必须要加vis了,这个vis判断就是如果anw[now]<dis就不看了,因为如果还看,这个点比如上次更新了A点的dist1,这次又去更新A点的dist2了,所以就不对了。
代码:
int T,n,m,k;
int va[N];
int in[N];
int dist1[N],dist2[N];
int anw[N];
vector<PII > e[N];
void bfs()
{
for(int i=1;i<=n;i++) dist1[i] = dist2[i] = anw[i] = inf;
priority_queue<PII,vector<PII>,greater<PII> > q;
for(int i=1;i<=n;i++)
{
if(in[i]<=1)
{
dist1[i] = dist2[i] = anw[i] = 0;
q.push({0,i});
}
}
while(q.size())
{
auto t = q.top();
q.pop();
int now = t.se,dis = t.fi;
if(dis>anw[now]) continue; //因为每个点是要从两个dist来判断,所以必须要判断了,因为如果now点上次已经被用过来了,你还是放进去肯定就不对了。因为第一次你更新了某个点的dist1,这次又会更新这个点的dist2。实际上你只能用一次。
for(auto tt:e[now])
{
int spot = tt.fi,w = tt.se;
if(dist1[spot]>dis+w)
{
dist2[spot] = dist1[spot];
dist1[spot] = dis+w;
}
else if(dist2[spot]>dis+w) dist2[spot] = dis+w;
if(dist1[spot]+dist2[spot]<anw[spot])
{
anw[spot] = dist1[spot]+dist2[spot];
q.push({anw[spot],spot});
}
}
}
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<n;i++)
{
int a,b,c;
cin>>a>>b>>c;
e[a].pb({b,c});
e[b].pb({a,c});
in[a]++,in[b]++;
}
bfs();
for(int i=1;i<=n;i++)
{
if(anw[i]!=inf) cout<<anw[i]<<" ";
else cout<<-1<<" ";
}
return 0;
}
总结:
多多思考呀,不能只靠感觉,还是要想想更优的策略对吧。