解题思路:
游览顺序为2 6 5 1,当跳过中间某个景点时(例如6)要花费的时间为总时间减去该景点与前一个景点的时间(2->6),再减去该景点与后一个景点的时间(6->5),最后再加上前一个景点的时间到后一个景点的时间(2->5),跳过第一个或者最后一个时只需要减去后一段或前一段时间;
注意事项:
参考代码:
先看暴力算法
#include#define ll long long using namespace std; typedef pair pii; const int N = 1e5+10; map id;//id[{x,y}]表示两个点x到y之间的距离 vectoredge[N];//edge[x]表示x点的邻接点数组 int n,m; ll ans = 0;//dfs函数中统计 x到y 时间 bool v[N];//是否访问过该点的标记 bool dfs(int st,int ed){//st起点,ed终点 for(int i=0;in>>m; int s[m]; for(int i=1;i>x>>y>>l; edge[x].push_back(y);//把y加入x的邻接点数组中 edge[y].push_back(x);//把x加入y的邻接点数组中 id[{x,y}] = id[{y,x}] = l;//记录x,y间的时间 } for(int i=0;i>s[i]; } ll sum = 0;//记录整个路线的总和时间 for(int i=0,j=1;j<m;i++,j++){//求整个路线的总和时间 ,依次传入两个点 ans = 0;//清除之前的数据 可以加入到dfs函数中 memset(v,false,sizeof(v)); //重置访问数组 v[s[i]] = true;//起点访问标记 dfs(s[i],s[j]); id[{s[i],s[j]}] = ans;//记录这两点的 时间 id[{s[j],s[i]}] = ans; sum+=ans;//加入sum } ans = 0; for(int x=0;x<m;x++){//x为游览路线中跳过点的下标 if(x==0){//第一个点:用总时间减去第一个点到他后面那个点的时间就是跳过顶点时 需要的时间 //例如1 2 3 4;不访问1时用总时间减去1到2的时间即为所求 cout<<sum - id[{s[x],s[x+1]}]<<" "; } else if(x==m-1){//最后点与上面同理 cout<<sum - id[{s[x-1],s[x]}]<<" "; } else{//中间的点就用总的时间减去该点前一个点到他的时间,再减去他到他后一个点的时间,再加上 //他前一个点到他后一个点的时间 //例如:2 6 5 1;跳过6时,用总时间减去2到6时间,再减去6到5时间,再加上2到5时间即为所求 ll temp1 = id[{s[x-1],s[x]}];//该点前一个点到他的时间 ll temp2 = id[{s[x],s[x+1]}];//他到他后一个点的时间 ans = 0; memset(v,false,sizeof(v)); v[s[x-1]] = true; dfs(s[x-1],s[x+1]); ll temp3 = ans;//他前一个点到他后一个点的时间 ll temp4 = sum - temp1 - temp2 + temp3;//所求时间 cout<<temp4<<" "; } ans = 0; } } int main(){ solve(); return 0; }
接下来是引入最小公共祖先(用的是数链剖分)优化后的代码,两者大体思路一致
#include#define ll long long using namespace std; typedef pair pii; const int N = 1e5+10; map id; vectoredge[N]; int n,m; ll sum[N];//sum[i]表示i这个点到我们指定的根节点u的时间 要开long long //树链剖分: int fa[N],son[N],sz[N];//fa[u]存u的父节点,son[u]u的重儿子,sz[u]u为根节点的子树节点数(包括自己) int top[N],dep[N];//top[u]指u所在的重链顶点(是一个轻儿子节点) dep[u]u所在的层级 //以上部分数组再后面都要初始化为0 void dfs1(int u,int father){//完善 fa[],sz[],dep[] fa[u] = father,dep[u] = dep[father] + 1,sz[u] = 1;//sz[u] = 1自己 for(int v : edge[u]){ if(v==father) continue; dfs1(v,u); sz[u] += sz[v];//节点大小加上子节点的大小 if(sz[son[u]] < sz[v]) son[u] = v; } } //初始调用dfs2(1,1) void dfs2(int u,int t){//完善top[] top[u] = t; if(!son[u]) return ;//没有重儿子就返回 dfs2(son[u],t);//如果有重儿子就深入遍历 连接这一条重儿子链top都为t for(int v : edge[u]) { if(v==fa[u] || v==son[u]) continue;//重儿子和父节点不在遍历 dfs2(v,v);//轻儿子节点的top为他自己 } } int lca(int u,int v){//找两个节点的最小公共祖先 while(top[u]!=top[v]){//u,v节点不在一条重链上 if(dep[top[u]] < dep[top[v]]) swap(u,v);//让u始终为重链顶点更深的那一方 u = fa[top[u]] ;//u向上跳 } return dep[u] < dep[v] ? u : v;//返回u,v层次更低的那一方 } //树链剖分--end void cal_sum(int u){//初始化sum数组 for(int v : edge[u]){ if(v==fa[u]) continue;//防止1到2又从2到1 //假设支链1->3->5,5到1的时间为3到5的时间加上3到1的时间 sum[v] = id[{v,u}] + sum[u]; cal_sum(v); } } void solve(){ //数据初始化 memset(sz,0,sizeof(sz)); memset(dep,0,sizeof(dep)); memset(son,0,sizeof(son)); memset(sum,0,sizeof(sum)); cin>>n>>m; int s[m]; for(int i=1;i>x>>y>>l; edge[x].push_back(y); edge[y].push_back(x); id[{x,y}] = id[{y,x}] = l; } for(int i=0;i>s[i]; } dfs1(1,0); dfs2(1,1); cal_sum(1);//指定1为根节点sum数组记录了每个点到1的时间 //数据初始化 end ll sum_ = 0;//原始浏览景点的总时间 for(int i=0;i<m-1;i++){ //此时任意两个点u,v之间的时间为: u到1的时间加上v到1的时间 //减去2倍的多余部分时间(多余部分是指最小公共祖先到1的时间) 注意乘上2 sum_ += sum[s[i]] + sum[s[i+1]] - 2*sum[lca(s[i],s[i+1])]; } //以下部分思路与暴力一致 for(int i=0;i<m;i++){ ll temp = sum_; if(i==0){ temp -= sum[s[i]] + sum[s[i+1]] - 2*sum[lca(s[i],s[i+1])]; } else if(i==m-1){ temp -= sum[s[i-1]] + sum[s[i]] - 2*sum[lca(s[i-1],s[i])]; } else{ temp -= sum[s[i-1]] + sum[s[i]] - 2*sum[lca(s[i-1],s[i])]; temp -= sum[s[i]] + sum[s[i+1]] - 2*sum[lca(s[i],s[i+1])]; temp += sum[s[i-1]] + sum[s[i+1]] - 2*sum[lca(s[i-1],s[i+1])]; } cout<<temp<<" "; } } int main(){ solve(); return 0; }
0.0分
4 人评分
永远的丰碑 (C语言代码)浏览:698 |
C二级辅导-公约公倍 (C语言代码)浏览:1550 |
奖学金 (C++代码)浏览:2053 |
C语言程序设计教程(第三版)课后习题6.1 (C语言代码)浏览:481 |
C语言程序设计教程(第三版)课后习题6.1 (C语言代码)浏览:545 |
蓝桥杯历届试题-九宫重排 (C++代码)浏览:2812 |
输出正反三角形 (C语言代码)格式错误!!!浏览:1177 |
A+B for Input-Output Practice (V) (C语言代码)浏览:640 |
C语言程序设计教程(第三版)课后习题8.9 (C语言代码)浏览:897 |
C语言程序设计教程(第三版)课后习题1.5 (C语言代码)浏览:566 |