A. Tower(暴力 + 看数据范围)

Problem - A - Codeforces

 

彭教授建造了n个不同高度的积木塔。第i座塔的高度为ai。

寿教授不喜欢这些塔,因为它们的高度是任意的。他决定首先精确地移除其中的m个,然后执行以下一些(或不执行)操作。

选择一座塔,将其高度ai增加1。
选择一座塔,把它的高度ai减少1。
选择一座塔,将其高度ai除以2,如果新的高度不是整数,则向下取整。
寿教授永远不能选择一个被移除的塔。如果在一次操作之后,塔的高度会变成0,那么这个操作是不允许的。在这些约束条件下,寿教授可以按照任意的顺序进行任意数量的操作。
寿教授希望所有未被移除的塔都有相同的高度。请计算出实现这一目标的最少操作数。

输入
第一行包含一个整数T(1≤T≤10),即测试案例的数量。

对于每个测试案例,第一行包含两个整数n,m (1≤n≤500,0≤m<n),即塔的数量,和塔的数量Prof.Shou在执行操作前应删除。

下一行包含n个整数a1,...,an(1≤ai≤109),即塔的初始高度。

输出
对于每个测试案例,在一行中输出最小的操作数。

例子
输入复制
3
2 0
2 6
5 0
1 2 3 4 5
5 3
1 2 3 4 5
输出拷贝
2
4
1

题解:
通过其给出的数据范围很小,我们应该推测出这并不是一个需要通过什么算法进行优化的题

我们直接暴力枚举所有可能的值

但是1e9数据范围也很大,那我们应该咋办呢?

其实可能值只会是每个塔的

	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		int t = a[i];
		while(t)
		{
			p[++idx] = t;
			t = t/2;
		}
	}

为什么是这样呢?

我们设最优解为t 与 t/2的两端中间的数

如果最优解在这部分因为我们已经把t/2存进去,所以答案要么是?-t/2,要么是t - ?

如果最优解比这个还要小

说明在t/2/2 ~ t/2之间,依次类推(结合完整代码后面看一下确实不太好理解)

#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<cstring>
#include<cmath>
using namespace std;
#define int long long
int a[505];
int p[100005];
int cnt[100060];
void solve()
{
	int n,m;
	cin >> n >> m;
	int idx = 0;
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		int t = a[i];
		while(t)
		{
			p[++idx] = t;
			t = t/2;
		}
	}
	int ans = 1e18;
	for(int i = 1;i <= n;i++)
	{
		int sum = 0;
		for(int j = 1;j <= n;j++)
		{
			cnt[j] = 1e18;
			if(p[i] > a[j])
			{
				cnt[j] = p[i] - a[j];
			}
			else if(p[i] == a[j])
			{
				cnt[j] = 0;
			}
			else
			{
				int k = a[j];
				int s = 0;
				while(k >= p[i])
				{
					if(k/2 <= p[i])
					{
						cnt[j] = min(k - p[i]+s,s + 1 + p[i] - k/2);
						break;
					}
					s++;
					k /= 2;
				}
			}
		}
		sort(cnt+1,cnt+n+1);
		for(int i = 1;i <= n - m;i++)
		{
			sum += cnt[i];
		}
		ans = min(ans,sum);
	}
	cout << ans<<"\n";
}
//32 2 4
//32 16 8 4 2 1
//2 1
//4 2 1
signed main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
//	cout.tie(0);
	int t = 1;
	cin >> t;
    while(t--)
	{
		solve();
	} 
}
//0 0 0 1 0 0 0 2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值