• BZOJ_3058_四叶草魔杖_kruscal+状压DP


    BZOJ_3058_四叶草魔杖_kruscal+状压DP

    Description

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

    Input

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

    Output

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

    Sample Input

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

    Sample Output

    30

    HINT

    对于 100% 的数据,2<=N<=16,0<=M<=N*(N-1)/2,0<=pi,qi<N,-1000<=Ai<=1000,0<=Ti<=1000,∑Ai=0。


    可以转化成这样一个问题:将原图划分成若干个连通块,使得每个连通块内权值和为0,求最小代价。

    由于n特别小可以状压,f[i]表示状态为i时最小的联通代价。

    对于每个和为0的集合,用kruscal求使这个集合连通的最小代价,然后DP即可。

    代码:

    #include <cstdio>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    int cal[1<<16],n,m,a[20],f[1<<16],g[1<<16],fa[20];
    struct E {
    	int x,y,z;
    	bool operator < (const E &u) const {
    		return z<u.z;
    	}
    }e[350];
    int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
    void dfs(int dep,int sta,int sum) {
    	if(dep==n) {
    		cal[sta]=sum; return ;
    	}
    	dfs(dep+1,sta|(1<<dep),sum+a[dep+1]);
    	dfs(dep+1,sta,sum);
    }
    void build(int sta) {
    	int i,num=0;
    	for(i=1;i<=n;i++) {
    		fa[i]=i; if(sta&(1<<(i-1))) num++;
    	}
    	int re=0,cnt=0;
    	for(i=1;i<=m;i++) {
    		int dx=find(e[i].x),dy=find(e[i].y);
    		if((sta&(1<<(dx-1)))&&(sta&(1<<(dy-1)))&&dx!=dy) {
    			fa[dx]=dy; re+=e[i].z; cnt++; if(cnt==num-1) break;
    		}
    	}
    	if(cnt==num-1) f[sta]=re;
    }
    int main() {
    	// freopen("shield.in","r",stdin);
    	// freopen("shield.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	int i,j;
    	for(i=1;i<=n;i++) scanf("%d",&a[i]);
    	dfs(0,0,0);
    	for(i=1;i<=m;i++) {
    		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z); e[i].x++; e[i].y++;
    	}
    	sort(e+1,e+m+1);
    	int mask=(1<<n)-1; 
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	for(i=1;i<=mask;i++) {
    		if(cal[i]==0) {
    			build(i);
    		}
    	}
    	for(i=0;i<=mask;i++) {
    		for(j=i&(i-1);j;j=i&(j-1)) {
    			if(f[j]<=1000000&&f[i-j]<=1000000) f[i]=min(f[i],f[j]+f[i-j]);
    		}
    	}
    	if(f[mask]<=10000000) printf("%d
    ",f[mask]);
    	else puts("Impossible");
    }
    /*
    3 3
    50 -20 -30
    0 1 10
    1 2 20
    0 2 100
    */
    
  • 相关阅读:
    力扣Leetcode 3. 无重复字符的最长子串
    力扣Leetcode 21. 合并两个有序链表
    力扣Leetcode 202. 快乐数 -快慢指针 快乐就完事了
    力扣Leetcode 面试题56
    力扣Leetcode 33. 搜索旋转排序数组
    力扣Leetcode 46. 全排列
    python123期末四题编程题 -无空隙回声输出-文件关键行数-字典翻转输出-《沉默的羔羊》之最多单词
    深入理解ReentrantLock的实现原理
    深入图解AQS实现原理和源码分析
    Java:CAS(乐观锁)
  • 原文地址:https://www.cnblogs.com/suika/p/9148726.html
Copyright © 2020-2023  润新知