当我遇到这个时,我正在竞争激烈的编码网站上解决问题。问题表明:
在这个游戏中有N级和M种可用武器。级别编号从0到N-1,武器编号从0到M-1。 您可以按任何顺序清除这些级别。在每个级别中,需要使用这些M武器的某些子集来清除此级别。如果在特定级别,你需要购买x新武器,你将支付x ^ 2个硬币。另请注意,您可以将当前拥有的所有武器提升到新的水平。最初,你没有武器。你能找出所需的最低硬币,以便清除所有级别吗?
输入格式 第一行输入包含2个空格分隔的整数: N =游戏中的等级数 M =武器类型的数量
接下来是N行。这些行的第i个包含长度为M的二进制字符串。如果是第j个字符 这个字符串是1,这意味着我们需要一个j型武器来清除第i级。
约束 1 <= N <= 20 1 <= M <= 20
输出格式 打印一个整数,这是问题的答案。
示例TestCase 1
输入
1 4
0101
输出
4个
解释 这场比赛只有一个级别。我们需要两种类型的武器--1和3.因为,最初是本 没有武器,他将不得不购买这些,这将花费他2 ^ 2 = 4个硬币。
示例TestCase 2
输入
3 3
111个
001
010
输出
3
解释 这场比赛有3个级别。 0级(111)需要所有3种类型的武器。第1级(001)只需要2型武器。第2级只需要1型武器。如果按给定顺序清除等级(0-1-2),则总成本= 3 ^ 2 + 0 ^ 2 + 0 ^ 2 = 9个硬币。如果我们按照1-2-0的顺序清除水平,那将是成本= 1 ^ 2 + 1 ^ 2 + 1 ^ 2 = 3个硬币,这是最佳方式。
方法
我能够弄清楚我们可以通过遍历二元弦来计算最低成本,以便我们在每个级别购买最少的武器。
一种可能的方法是遍历二进制字符串数组并计算每个级别的成本,同时数组已按正确的顺序排列。正确的顺序应该是当字符串已经被排序时,即上述测试用例的情况下为001,010,111。按此顺序遍历数组并总结每个级别的成本给出了正确的答案。
此外,java中的sort方法可以在对数组运行循环之前对这些二进制字符串进行排序,以总结每个级别的开销。
Arrays.sort(weapons);
这种方法适用于某些测试用例,但是超过一半的测试用例仍然失败,我无法理解我的逻辑是什么问题。我使用按位运算符来计算每个级别所需的武器数量并返回它们的平方。
不幸的是,我看不到失败的测试用例。非常感谢任何帮助。
答案 0 :(得分:4)
这可以通过动态编程来解决。
国家将是我们目前拥有的武器的掩码。
过渡将尝试从当前状态依次清除每个n
可能的等级,获得我们需要的额外武器并支付它们。
在每个n
结果状态中,我们采用当前方式的最低成本来实现它以及之前观察到的所有方式。
当我们已经拥有一些武器时,某些级别实际上不需要购买额外的武器;这种转变将被自动忽略,因为在这种情况下,我们到达同一个州支付相同的费用。
我们从m
零开始,已经付费0
。
结束状态是所有给定级别的按位OR,到达那里的最低成本就是答案。
在伪代码中:
let mask[1], mask[2], ..., mask[n] be the given bit masks of the n levels
p2m = 2 to the power of m
f[0] = 0
all f[1], f[2], ..., f[p2m-1] = infinity
for state = 0, 1, 2, ..., p2m-1:
current_cost = f[state]
current_ones = popcount(state) // popcount is the number of 1 bits
for level = 1, 2, ..., n:
new_state = state | mask[level] // the operation is bitwise OR
new_cost = current_cost + square (popcount(new_state) - current_ones)
f[new_state] = min (f[new_state], new_cost)
mask_total = mask[1] | mask[2] | ... | mask[n]
the answer is f[mask_total]
复杂度为O(2^m * n)
时间和O(2^m)
内存,对于大多数在线评委中的m <= 20
和n <= 20
来说应该没问题。
答案 1 :(得分:0)
这个问题背后的逻辑是,每次你必须找到对应于二进制字符串的最小设置位数,该二进制字符串将包含到目前为止所在级别的武器。
对于前: 我们有数据作为 4 3 101-2位 010-1位 110-2位 101-2位 现在,当010具有最小位时,我们首先计算它的成本,然后更新当前模式(通过使用按位OR),因此当前模式为010 接下来,我们找到当前模式的下一个最小设置位 我已经使用逻辑首先使用XOR作为当前模式和给定数字然后使用AND与当前数字(A ^ B)&amp; A 所以这些位在操作后变得像这样 (101 ^ 010)&amp; 101-> 101-2位 (110 ^ 010)&amp; 110-> 100-1位 现在我们知道min位是110我们选择并计算成本,更新模式等等。 此方法返回相对于当前模式的字符串成本
private static int computeCost(String currPattern, String costString) {
int a = currPattern.isEmpty()?0:Integer.parseInt(currPattern, 2);
int b = Integer.parseInt(costString, 2);
int cost = 0;
int c = (a ^ b) & b;
cost = (int) Math.pow(countSetBits(c), 2);
return cost;
}
答案 2 :(得分:0)
@Gassa的动态优化理念可以通过估算剩余成本的最小值和最大值来使用A*进行扩展,其中
minRemaining(s)=bitCount(maxState-s)
maxRemaining(s)=bitCount(maxState-s)^2
从优先级队列开始 - 以成本+ minRemaining为基础 - 仅使用空状态,然后从此队列中替换未达到maxState的状态,最多n个基于n个级别的新状态:
跟踪bound=min(cost(s)+maxRemaining(s))
在队列中,
并使用bitCount(maxState)^2+1
extract state with lowest cost
if state!=maxState
remove state from queue
for j in 1..n
if (state|level[j]!=state)
cost(state|level[j])=min(cost(state|level[j]),
cost(state)+bitCount(state|level[j]-state)^2
if cost(state|level[j])+minRemaining(state|level[j])<=bound
add/replace state|level[j] in queue
else break
这个想法是跳过死胡同。因此,请考虑评论中的示例
11100 cost 9 min 2 max 4
11110 cost 16 min 1 max 1
11111 cost 25 min 0 max 0
00011 cost 4 min 3 max 9
bound 13
remove 00011 and replace with 11111 (skipping 00011 since no change)
11111 cost 13 min 0 max 0
11100 cost 9 min 2 max 4
11110 cost 16 min 1 max 1
remove 11100 and replace with 11110 11111 (skipping 11100 since no change):
11111 cost 13 min 0 max 0
11110 cost 10 min 1 max 1
bound 11
remove 11110 and replace with 11111 (skipping 11110 since no change)
11111 cost 11 min 0 max 0
bound 11
在最坏的情况下,操作次数应该类似于动态优化,但在很多情况下会更好 - 而且我不知道最坏的情况是否会发生。