zoukankan      html  css  js  c++  java
  • TYVJ 2054 [Nescafé29]四叶草魔杖 最小生成树 状态压缩/背包DP

    $ ightarrow $ 戳我进TYVJ原题

    [Nescafé29]四叶草魔杖

    题目限制

    时间限制 内存限制 评测方式 题目来源
    1000ms 131072KiB 标准比较器 Local

     

    题目背景

    陶醉在彩虹光芒笼罩的美景之中,探险队员们不知不觉已经穿过了七色虹,
    到达了目的地,面前出现了一座城堡和小溪田园,城堡前的木牌上写着“Poetic Island”。
     
    “这一定就是另外两位护法的所在地了……我们快进去吧!”
     
    探险队员们快步进入了城堡,城堡大厅的羊毛沙发上坐着两个人。
     
    “你们是Nescafe的护法吧?”
     
    “是的哦~ 我们就是圣剑护法rainbow和魔杖护法freda~ 你们来这里做什么呢~”
     
    “我们是来拜访圣主和四位护法的……”
     
    “可是圣主applepi已经前往超自然之界的学校(Preternatural Kingdom University,简称PKU)修炼魔法了,
    要想见到他,必须开启Nescafe之塔与超自然之界的通道。但是圣主规定,开启通道的方法不能告诉任何外人。
    我只能提示你们,开启通道的钥匙就与四位护法有关T_T”
     
    探险队员环视四周,突然,其中一人的目光停留在了魔杖之上。
    “hoho~ 魔杖!传说中开启异时空通道的钥匙不就叫四叶草魔杖吗?
    四叶草有力量、信心、希望和幸运四片叶子,护法恰好有神刀、飞箭、圣剑、魔杖四位!aha~我找到答案了!”
     
    “好吧,那我们就满足你们的愿望~”
     

    题目描述

    魔杖护法Freda融合了四件武器,于是魔杖顶端缓缓地生出了一棵四叶草,四片叶子幻发着淡淡的七色光。
    圣剑护法rainbow取出了一个圆盘,圆盘上镶嵌着 $ N $ 颗宝石,编号为 $ 0~N-1 $ 。
    第i颗宝石的能量是 $ A_i $ 。
    如果 $ A_i>0 $ ,表示这颗宝石能量过高,需要把Ai的能量传给其它宝石;
    如果 $ A_i<0 $ ,表示这颗宝石的能量过低,需要从其它宝石处获取 $ -A_i $ 的能量。
    保证 $ ∑A_i =0 $ 。只有当所有宝石的能量均相同时,把四叶草魔杖插入圆盘中央,才能开启超自然之界的通道。
    不过,只有 $ M $ 对宝石之间可以互相传递能量,其中第 $ i $ 对宝石之间无论传递多少能量,都要花费 $ T_i $ 的代价。
    探险队员们想知道,最少需要花费多少代价才能使所有宝石的能量都相同?
     

    输入格式

    第一行两个整数 $ N、M $ 。
    第二行 $ N $ 个整数 $ A_i $ 。
    接下来 $ M $ 行每行三个整数 $ p_i,q_i,T_i $ ,表示在编号为 $ p_i $ 和 $ q_i $ 的宝石之间传递能量需要花费 $ T_i $ 的代价。
    数据保证每对 $ p_i、q_i $ 最多出现一次。
     

    输出格式

    输出一个整数表示答案。无解输出Impossible。
     

    提示

    对于 $ 50 $ % 的数据,$ 2 le N le 8 $ 。
    对于 $ 100 $ % 的数据,$ 2 le N le 16,0 le M le N*(N-1)/2,0 le p_i,q_i<N,-1000 le A_i le 1000,0 le T_i le 1000,∑A_i=0 $ 。
     

    样例数据

    输入样例

     3 3
     50 -20 -30
     0 1 10
     1 2 20
     0 2 100
    

    输出样例

     30
    

     

    题解

    • 每个 $ sum A_i =0 $ 的子图内传递能量的最小代价是其最小生成树

    • 最终整个图可能分成若干块 $ sum a_i =0 $ 的子图分别传递

    • 以 $ sum a_i =0 $ 的子图为物品做二进制集合背包的状态压缩 $ DP $
       

    代码

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define inf 1000000007
    struct edge{ int u,v,w; }e[205];
    int n,m,a[20],f[1<<20],g[1<<20],fa[20],tot;
    bool vis[20];
    bool cmp(edge x,edge y){ return x.w<y.w; }
    int calc(int x){
    	int res=0;
    	for(int i=1;i<=n;++i) if(x&(1<<i-1)) res+=a[i];
    	return res;
    }
    int find(int x){
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x];
    }
    int kruskal(int x){
    	int num=0,res=0,tmp=0;
    	memset(vis,0,sizeof(vis));
    	for(int i=1;i<=n;++i){
    		fa[i]=i;
    		if(x&(1<<(i-1))) vis[i]=1,++num;
    	}
    	for(int fu,fv,i=1;i<=m;++i){
    		if(!vis[e[i].u]||!vis[e[i].v]) continue;
    		fu=find(e[i].u); fv=find(e[i].v);
    		if(fu==fv) continue;
    		fa[fu]=fv;
    		res+=e[i].w;
    		++tmp; if(tot==num-1) break;
    	}
    	if(tmp!=num-1) return inf;
    	return res;
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	for(int i=1;i<=m;++i){
    		scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
    		++e[i].u; ++e[i].v;
    	}
    	sort(e+1,e+1+m,cmp);
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	for(int i=1;i<=(1<<n)-1;++i)
    		if(calc(i)==0){
    			int tmp=kruskal(i);
    			if(tmp!=inf) g[++tot]=i; f[i]=tmp;
    		}
    	for(int i=0;i<=(1<<n)-1;++i)
    		for(int j=1;j<=tot;++j)
    			if((i&g[j])==0) f[i|g[j]]=min(f[i|g[j]],f[i]+f[g[j]]);
    	if(f[(1<<n)-1]==inf) puts("Impossible");
    	else printf("%d",f[(1<<n)-1]);
    	return 0;
    }
    
  • 相关阅读:
    多线程创建方式及线程安全问题
    JDBC连接池&DBUtils
    mySQL 多表查询语句
    git 本机链接多库配置
    mysql类似递归的一种操作进行层级查询
    js 自定义事件观察者模式(发布/订阅)
    CSS样式遇见的问题总结记录
    maven打包pom.xml备忘
    JasperReports实现报表调出excel
    ActiveMQ 集群配置 高可用
  • 原文地址:https://www.cnblogs.com/PotremZ/p/TYVJ2054.html
Copyright © 2011-2022 走看看