zoukankan      html  css  js  c++  java
  • 【Gym101137K】Knights of the Old Republic(生成树 DP)

    题目链接

    大意

    给定(N)个点(M)条边的一张图,其中:
    每个点有两个属性(A_i,B_i),表示你需要至少(A_i)个士兵来攻占该点,而空投一个士兵至该点需要Bi的花费。
    每条边都有一个属性(C_i),表示如果该边的两个端点的士兵数量之和大于了(C_i),那么这条边就被打通了,即士兵可以自由通过该边。

    求:攻占过所有点的最小代价。
    (1le N le 3cdot 10^5)

    思路

    首先,最小生成树经典算法Kruskal直接套,把边按值从小到大排序。

    考虑一条边所连接的两个连通块如何合并。
    (Dp[i])表示点集(i)被攻占完的最下代价,则:
    (Dp[s+t])的值为Min((Dp[s]+Dp[t]),打通这条边情况下的最小值)

    贪心地想,若这两个连通块总共需要(K)个士兵,则这(K)个士兵一定是从这两个连通块中有着最小的(A)值的点空降的,这样可以满足代价最小。
    同时,这样放也可以利用Kruskal的性质:之前放的边值一定都小于当前边值,所以如果要加入这条边,那么之前的边一定都会被打通。

    综上:
    先把所有边按边权排序,然后在不断合并两个连通块的同时,
    动态维护连通块内的最小花费,最大需求与答案值。

    出解。

    代码

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN=300005;
    const long long ONE=1;
    int N,M,Fa[MAXN];
    int Mx[MAXN],Mi[MAXN];
    struct Edge{int x,y,z;}s[MAXN];
    bool cmp(Edge A,Edge B){return A.z<B.z;}
    int Find(int x){return Fa[x]==x?x:Fa[x]=Find(Fa[x]);}
    long long Ans,Dp[MAXN];
    int main(){
    	scanf("%d%d",&N,&M);
    	for(int i=1,x,y;i<=N;i++){
    		scanf("%d%d",&x,&y);
    		Fa[i]=i;Dp[i]=ONE*x*y;
    		Mi[i]=y;Mx[i]=x;
    	}
    	for(int i=1;i<=M;i++)
    		scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].z);
    	sort(s+1,s+M+1,cmp);
    	for(int i=1;i<=M;i++){
    		int x=Find(s[i].x);
    		int y=Find(s[i].y);
    		if(x==y)continue;Fa[x]=y;
    		Mi[y]=min(Mi[x],Mi[y]);
    		Mx[y]=max(s[i].z,max(Mx[x],Mx[y]));
    		Dp[y]=min(Dp[x]+Dp[y],ONE*Mi[y]*Mx[y]);
    	}
    	for(int i=1;i<=N;i++)
    		if(Find(i)==i)Ans+=Dp[i];
    	printf("%lld
    ",Ans);
    }
    
  • 相关阅读:
    树莓派linux驱动学习之hello world
    android通过服务实现消息推送
    [转载] iOS开发分辨率那点事
    《裸辞的程序猿漂流记十四》——升级站点
    typedef 总结
    苹果 App Store 申请和管理相关知识
    判断系统是12小时制还是24小时制
    解决 UIView 设置背景为UIImage图片变型问题[XXX setBackgroundColor:[UIColor colorWithPatternImage:XXX]];
    免费iOS第三方推送工具Urban Airship使用教程
    sizeWithFont:方法使用明细
  • 原文地址:https://www.cnblogs.com/ftotl/p/11768252.html
Copyright © 2011-2022 走看看