zoukankan      html  css  js  c++  java
  • Codeforces 806 D. Perishable Roads Dijkstra

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF806D.html

    题目传送门 - CF806D

    题意

      给定一个 n 个点的无向完全图,每一条边有一定的边权。

      对于它的一个生成树,我们定义一个节点的花费为该点到根的边权min 。

      一个生成树的权值为所有节点的花费之和。

      对于每一个节点,求出以他为根的最小生成树权值。

      $nleq 2000$

    题解

      首先,我们考虑找出权值最小的边。

      那么,一旦这条边被连到了根,剩下的所有点直接连向它就好了。

      假设有一个根,那么最优解之一 一定长成这样:

      其中根为最上面那个点,黑色点的上面直接连接的边权值为 min 。

      我们将所有点的权值先减掉min 。

      这之后我们只需要求出“将根和任意一个连接 0 边的节点连通的最小花费”即可。

      

      考虑将从黑色节点到根的边权依次写出来,假设是 $w_1,w_2,w_3,cdots ,w_n$ ,那么有这样一个性质:

      最优解之一 一定满足 $forall i>1, w_i<w_{i+1}$ 。

      因为如果存在一个不满足的 $i$ ,我们可以直接把第 $i-1$ 个接上去,把第 $i$ 个接到黑点上面。

      对于第一条边,当然不需要满足。

      设一个超级源点 S ,使他免费连向所有与 0 边直接相连的点,并与其他点没有边。

      于是,对于任何一个点 x,对于它连向的任意一个与 0 边直接相连的点 y ,将 d[x][S] 变成 min(d[x][S],2*d[x][y]) 即可。

      最后只要从 S 开始跑一下最短路就好了。

     

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=2005;
    const int INF=2e9;
    int n;
    int g[N][N];
    int tag[N],near[N];
    int vis[N];
    LL dis[N];
    int main(){
    	n=read();
    	int Min=INF;
    	for (int i=1;i<=n;i++)
    		for (int j=i+1;j<=n;j++){
    			g[i][j]=g[j][i]=read();
    			Min=min(Min,g[i][j]);
    		}
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			if (i!=j){
    				g[i][j]-=Min;
    				if (!g[i][j])
    					tag[i]=tag[j]=1;
    			}
    	int S=n+1;
    	for (int i=1;i<=n;i++)
    		g[S][i]=g[i][S]=tag[i]?0:INF;
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			if (i!=j){
    				if (tag[i])
    					near[j]=1;
    				if (tag[j])
    					near[i]=1;
    			}
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			if (i!=j&&near[j])
    				g[S][i]=g[i][S]=min(g[i][S],2*g[i][j]);
    	for (int i=1;i<=n+1;i++)
    		vis[i]=0,dis[i]=1e15;
    	dis[S]=0;
    	for (int i=1;i<=n+1;i++){
    		int x=0;
    		for (int j=1;j<=n+1;j++)
    			if (!vis[j]&&(x==0||dis[j]<dis[x]))
    				x=j;
    		vis[x]=1;
    		for (int j=1;j<=n+1;j++)
    			if (!vis[j]&&dis[j]>dis[x]+g[x][j])
    				dis[j]=dis[x]+g[x][j];
    	}
    	for (int i=1;i<=n;i++)
    		cout << dis[i]+(LL)Min*(n-1) << endl;
    	return 0;
    }
    

      

  • 相关阅读:
    【Go】windows下搭建go语言编译环境
    【java回调】同步/异步回调机制的原理和使用方法
    【tomcat】tomcat远程调试
    【tomcat】获取访问者真实IP
    【深度学习学习记录】之一:开篇闲扯一些话
    【java】线程安全的整型类AtomicInteger
    【OpenStack】源码级深入了解删除虚拟机操作
    【Maven】maven的常用命令以及搭建maven私人仓库
    素 数 (第三届省赛)
    房间安排(第三届省赛)
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF806D.html
Copyright © 2011-2022 走看看