zoukankan      html  css  js  c++  java
  • 图论

    2021 1|2

    10064. 「一本通 3.1 例 1」黑暗城堡

    mind

    这个题显然是求最短路径树的个数

    最短路径树:对于根节点u,它到任意v的最小距离等于树上的距离

    考虑(dijkstra),每次选择一个与起始节点最小的点加入集合,所以选择的边组成的集合就是一种最短路径树,至于统计方案,我们可以判断每一条边,如果(dis[v]=dis[u]+e[i])方案数可以累乘,不过如果更新(dijkstra)需要重置到这一点的最短路方案数。当然我们也可以在跑完(dijkstra)后统计答案。

    /*
    work by:Ariel_
    */
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <queue>
    #include <cstring>
    #define int long long 
    using namespace std;
    const int M = 5e5;
    const int inf = 0x3f3f3f;
    int n,m,mod,js,head[1005];
    struct edge{
       int u,v,w,nxt;
    }e[M << 1];
    void add_edge(int u,int v,int w){
          e[++js].v = v;
          e[js].w = w;
          e[js].nxt = head[u];
          head[u] = js;
    }
    struct node{
    	int u,w;
    	bool operator < (const node a)const{
    	     return w > a.w;
    	}
    };
    bool vis[1005];
    int dis[1005],ans[1005];
    priority_queue<node> q;
    void dij(int s){
    	memset(dis, inf,sizeof(dis));
    	q.push((node){s,0});
    	dis[s] = 0;ans[1] = 1;
    	while(!q.empty()){
    		node tp = q.top();q.pop();
    		int u = tp.u;
    		if(vis[u])continue;
    		vis[u] = 1;
    		for(int i = head[u]; i; i = e[i].nxt){
    			 int v = e[i].v;
    			 if(dis[v] > dis[u] + e[i].w){
    			 	 ans[v] = 1;dis[v] = dis[u] + e[i].w;
    			 	 q.push((node){v,dis[v]});
    			 }
    			 else if(dis[v] == dis[u] + e[i].w)
    			 	  ans[v]++;
    		}
    	}
    	return;
    }
    signed main(){
       std::ios::sync_with_stdio(false);
       mod = pow(2,31) - 1;
       cin>>n>>m;
       for(int i = 1,u,v,w;i <= m; i++){
       	   cin>>u>>v>>w;
       	   add_edge(u, v, w);
       	   add_edge(v, u, w);
       }
       dij(1);
       int Ans = 1;
       for(int i = 1;i <= n; i++){
       	   Ans *= ans[i]%mod;
       	   Ans %= mod;
       }
       cout<<Ans%mod;
    }
    
    

    一本通 3.1 例 2」北极通讯网络

    mind

    逆向思维,把所有可以相互连通的村庄连接起来,构成一个图,卫星台数就是图中连通支的个数。

    问题转化为:找到一个最小的(d),使得把所有权值大于(d)的边去掉之后,连通支的个数(<=k)

    定理1

    如果去掉所有权值大于d的边后,最小生成树被分割成为(k)个连通支,则图也被分割成为(k)个连通支

    证明用反证法

    所以该题的解就是生成树中第k长的边

    注意定义类型,把ans数组定义成了int类型调了半个小时

    /*
    work by:Ariel_
    */
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    const int M = 510;
    const int N = 1e6 + 4;
    int n,k,fa[M],cnt,js;
    double ans[M];
    struct Pots{
        int x,y;
    }p[N << 1];
    struct node{
       int xx,yy;
       double dis;
    }a[N<<1];
    double calc(int x_1,int x_2,int y_1,int y_2){
       return sqrt((x_1 - x_2)*(x_1 - x_2) + (y_1 - y_2)*(y_1 - y_2));
    }
    int find(int x){
       if (fa[x] != x)fa[x] = find(fa[x]);
       return fa[x];
    }
    bool cmp(node a,node b){
        return a.dis < b.dis;
    }
    int main(){
       
       cin>>k>>n; 
       
       if(k > n){
       	  printf("0.00");
       	  return 0;
       }
       
       for(int i = 1;i <= n; i++) cin>>p[i].x>>p[i].y;
       for(int i = 1;i <= n; i++) fa[i] = i;   
       for(int i = 1;i <= n; i++)
         for(int j = i + 1;j <= n; j++){
         	  a[++cnt] = (node){i,j,calc(p[i].x,p[j].x,p[i].y,p[j].y)};
    	 }
    
        sort(a + 1,a + cnt + 1,cmp);
      
        for(int i = 1; i <= cnt; i++){//构造最小生成树 
        	 int x = find(a[i].xx),y = find(a[i].yy);
        	 if(x != y){
        	 	fa[x] = y;
        	 	ans[++js] = a[i].dis; 
    		 }
        }
    	printf("%.2lf",ans[js - k + 1]);
    }
    
    

    10066. 「一本通 3.1 练习 1」新的开始

    mind

    最小生成树

    由于每个矿井建修电站都有费用(可看作有权值的点),贪心优先选择权值最小的点,然后构建最小生成树,若两者连线消耗比要连接的点的权值小,更新这个要连的点的权值,同时ans记录答案

    由于数据很水,所以用链接矩阵存图

    /*
    work by:Ariel_
    */
    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int M = 1e5;
    const int inf = 0x3f3f3f3f;
    int n,di[301],vis[301],minn,k,ans;
    int js,head[301],e[301][301];
    
    int main(){
       std::ios::sync_with_stdio(false);
       cin>>n;
       for(int i = 1;i <= n; i++)cin>>di[i];
       
       for(int i = 1;i <= n; i++)
       	  for(int j = 1,w;j <= n;j++){
       	       cin >>w;
    		   e[i][j] = w; 
    	   }
    	
       for(int i = 1;i <= n; i++){
       	   minn = inf;
       	   
       	   for(int j = 1;j <= n; j++)
       	     if(!vis[j] && minn > di[j])
       	        minn = di[j],k = j;
    			     	 
    	    vis[k] = 1;ans += minn;
    	    
    	    for(int j = 1;j <= n; j++){
    	      if(!vis[j] && di[j] > e[j][k])
    		     di[j] = e[j][k];
    		}
       }
       printf("%d",ans);
    }
    
    

    10067. 「一本通 3.1 练习 2」构造完全图

    mind

    完全图:有(n)个点,(frac{n*(n-1)}{2})条边构成的图

    要求仅有一条最小生成树,所以加边的时候一定比当前边要小

    (prim):把所有的点分为两个集合,(A),(B),起初(A)集合中只有一个点,然后从(B)每拿一个点就要添加

    (cnt[x] * cnt[y] - 1)条边(x , y分别为两点的父亲节点(并查集)),所加的边至少要比当前的边大1.否则不满足仅有一个最小生成树的条件(模一下过程就好了)

    /*
    work by:Ariel_
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define int long long
    using namespace std;
    const int M = 1e5 + 2;
    int n,fa[M],ans,cnt[M];
    struct edge{
       int u,v,w;
    }e[M];
    
    int find(int x){
        if(fa[x]!=x)return fa[x]=find(fa[x]);
        else return fa[x];
    }
    
    bool cmp(edge a,edge b){return a.w<b.w;}
    
    signed main(){
       std::ios::sync_with_stdio(false);
       cin >> n;
       for(int i = 1; i <=n - 1; i++){
       	   cin>>e[i].u>>e[i].v>>e[i].w;
       	   ans += e[i].w; 
       }
       for(int i = 1;i <= n; i++){  
           fa[i] = i;cnt[i] = 1;
       } 
       sort(e + 1,e + n,cmp);
       for(int i = 1; i < n; i++){
       	   int x = find(e[i].u),y = find(e[i].v);
       	   if(x != y){
    		 ans += (cnt[x]*cnt[y] - 1)*(e[i].w + 1);
       	     fa[y] = x;
    		 cnt[x] += cnt[y];	
    	   }
       }
       printf("%lld",ans);
    }
    
    
  • 相关阅读:
    redis的事务不是原子性
    Jenkins持续集成 入门实践
    Docker入门实践
    程序员该有的职业素养
    ubuntu sudoers配置错误
    日志分析工具 Log Parser
    压力测试记录
    Winscp使用sudo user登录
    Linux下通过NFS共享文件夹
    Jexus 5.4.6 on CentOS 6.6
  • 原文地址:https://www.cnblogs.com/Arielzz/p/14224737.html
Copyright © 2011-2022 走看看