zoukankan      html  css  js  c++  java
  • [第三场T3]Kronican

    题目描述

    Mislav有N个无限体积的杯子,每一个杯子中都有一些水。Mislav想喝掉所有的水,但他不想喝超过K杯水。Mistrav能做的就是将一个杯子中的水倒入另一个杯子中。 不幸的是,挑选哪两个杯子进行倒水操作对Mislav来说很重要,因为并非所有的杯子都离他一样远。更准确地说,从i号杯子向j号杯子倒水所付出的代价为Cij。 帮助Mislav找到他需要付出的总代价的最小值。

    输入

    第一行输入包含整数N和K(1≤K≤N≤20)。表示水杯的总数和Mislav最多能喝多少杯。 接下来N行每行包含N个整数Cij(0≤Cij≤1e5)。第i+1行的第j个整数表示从第i个杯子第j个杯子倒水所需要付出的代价。保证Cii等于0。

    输出

    输出一个整数。表示Mislav需要付出的总代价的最小值。

    样例输入

    5 2
    0 5 4 3 2
    7 0 4 4 4
    3 3 0 1 2
    4 3 1 0 5
    4 5 5 5 0

    样例输出

    5

    解题思路

    n比较小,每个杯子只有有水无水两种情况,可用01字符串表示,很明显看出来状压了。

    然而当时考我还不会。看着这道题我以为是最小生成树,然而后面发现不对劲,因为有些点你倒出去的不可能再有其他杯子倒进来,这样不可能最优。也不可能还可以倒到另外一些杯子,所以最小生成树是有问题的。当时没管这么多,搞了个裸的最小生成树,想要骗分,一开始本来写了判断点可不可以再连边的,后面删了,然而旁边那个人没删,骗了80分......

    讲正解了。

    其实也挺容易

    dp[s - (1 << (i - 1))] = min(dp[s - (1 <<( i - 1))],dp[s] + a[i][j])

    其实就相当于在s状态下把i号杯子的水倒入j号杯子。

    那么s是啥,在本题s状态其实就是一个二进制数用十进制表示的状态,0表示当前杯子无水,1表示当前杯子有水。

    所有杯子所组成的状态便是s

    举个例子

    如果共有6个杯子 1 3 5 号有水,其余无水。那么整个二进制便是010101(从6到1连起来)

    十进制数便是21。

    dp差不多是这样,但这个dp好像挺难实现的。写法其实感觉挺奇葩的,很不能理解。

    如果你用1表示有水的话

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<queue>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int n,k,a[25][25];
    int dp[2000005],ans = 0x3f3f3f3f;
    int check(int x){
    	int ret = 0;
    	while (x){
    		if (x % 2)
    			ret ++;
    		x /= 2;
    	}
    	return ret;
    }
    int main(){
    	scanf ("%d%d",&n,&k);
    	for (int i = 1;i <= n;i ++)
    		for (int j = 1;j <= n;j ++)
    			scanf ("%d",&a[i][j]);
    	memset(dp,0x3f,sizeof(dp));
    	dp[(1 << n) - 1] = 0;//都有水,花费为0
    	for (int s = (1 << n) - 1;s >= 1;s --){//枚举当前状态(2进制01表示,这里转换为10进制)
    		for (int i = 0;i < n;i ++){//要倒出的杯子
    			if ((s & (1 << i))){//当前状态下,倒出杯子中的水不为空
    				for (int j = 0;j < n;j ++){
    					if (i != j && (s & (1 << j))){//当前状态下,倒入杯子处的水不为空(为空无意义)并且不相同。
    						dp[s - (1 << i)] = min(dp[s - (1 << i)],dp[s] + a[i + 1][j + 1]);//状态转移
    					}
    				}
    			}
    		}
    	}
    	for (int s = 0;s < (1 << n);s ++){//找答案
    		if (check(s) == k)
    			ans = min(ans,dp[s]);
    	}
    	printf("%d",ans);
    }

    如果1表示无水的话

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<queue>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int n,k,a[25][25];
    int dp[2000005],ans = 0x3f3f3f3f;
    int check(int x){
    	int ret = 0;
    	while (x){
    		if (x % 2)
    			ret ++;
    		x /= 2;
    	}
    	return ret;
    }
    int main(){
    	scanf ("%d%d",&n,&k);
    	for (int i = 1;i <= n;i ++)
    		for (int j = 1;j <= n;j ++)
    			scanf ("%d",&a[i][j]);
    	for (int i = 1;i < (1 << n);i ++)//边界,dp[0] = 0
    		dp[i] = 0x3f3f3f3f;
    	for (int s = 0;s < (1 << n);s ++){//枚举当前状态(2进制01表示,这里转换为10进制)
    		for (int i = 0;i < n;i ++){//要倒出的杯子
    			if (!(s & (1 << i))){//当前状态下,倒出杯子中的水不为空
    				for (int j = 0;j < n;j ++){
    					if (i != j && !(s & (1 << j))){//当前状态下,倒入杯子处的水不为空(为空无意义)并且不相同。
    						dp[s^(1 << i)] = min(dp[s^(1 << i)],dp[s] + a[i + 1][j + 1]);//状态转移
    					}
    				}
    			}
    		}
    	}
    	for (int s = 0;s < (1 << n);s ++){
    		if (check(s) == n - k)
    			ans = min(ans,dp[s]);
    	}
    	printf("%d",ans);
    }
  • 相关阅读:
    基于微信小程序的票价和时间选择以及计算总价
    基于Echarts的股票K线图展示
    基于Echarts的中国地图数据展示
    微信公众号网页授权登录获取用户基本信息
    springboot+mybatis+maven角色权限框架
    java服务端微信小程序支付
    推理 —— 猜帽子颜色
    Java 容器的使用及数组、List、Set 的相互转换
    构建工具 —— Groovy 与 Gradle
    效率生产力工具 —— idea 插件
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566673.html
Copyright © 2011-2022 走看看