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;
    }
    
  • 相关阅读:
    linux内核中GNU C和标准C的区别
    linux内核中GNU C和标准C的区别
    Getting start with dbus in systemd (02)
    Getting start with dbus in systemd (01)
    Getting start with dbus in systemd (03)
    物理内存相关的三个数据结构
    数据类型对应字节数(32位,64位 int 占字节数)
    Linux kernel 内存
    共模电感的原理以及使用情况
    [原创]DC-DC输出端加电压会烧毁
  • 原文地址:https://www.cnblogs.com/PotremZ/p/TYVJ2054.html
Copyright © 2011-2022 走看看