您的位置:首页 > 其它

CF #312.div2 -Amr and Chemistry-暴力+位运算

2015-08-18 11:16 375 查看
题意:给出n个数,让你通过下面两种操作,把它们转换为同一个数。求最少的操作数。

1.ai = ai*2

2.ai = ai/2,向下取整

没什么思路,后来看到别人是这样暴力过的:

用左移右移来代替乘除2

vis[i] 表示 i 这个数可以由几个数转化而来

cnt[i] 表示题目给出的 n 个数全部转化为 i 需要的操作数。

设输入n个数最大为maxx

对于最后转化的结果的范围 ,应该是1 到 2*maxx (0或大于maxx*2都不是最优的,)

所以vis和cnt数组都开[2*maxx]

左移操作:

对于一个数,不断左移,每次左移,把等到的数组x,标记vis[X]++,cnt[x]+=step ,step为第一次到当前的步数,直到超maxx

右移操作:

(重复下面两操作直到该数为0)

若一个数最低位为1,先右移一步,再复制该数,再不断左移,上限为maxx; (因为不先右移一步得到的数就和左移操作中重复了)

若一个数最低位为0,右移一步,每一步更新得到的数的个数和步数 ()

这样我们就把每个数 的所有 移动情况都处理出来了,并且是最少的步数。

然而遍历数组 ,找vis【I】==n, 且cnt[i]最小的就是答案

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include<bitset>
#include <map>
#include <set>
#include <vector>
using namespace std;
#define INF 0x7f7f7f7f
const __int64 maxn = 100060;  
int max(int a,int b)
{return a>b?a:b;} 
int min(int a,int b)
{return a<b?a:b;} 
int vis[2*maxn	];
int cnt[maxn*2];
int maxx=0;

int slove(int x)
{
	vis[x]++;
	int temp1=x;
	int temp2=x;
	int step1=0;
	int step2=0;

	while(temp1<=maxx)
	{
		temp1<<=1;
		step1++;
		vis[temp1]++;
		cnt[temp1]+=step1;
	}
	
	while(temp2)
	{
		if (temp2&1 && temp2!=1) //最低位为1
		{
			temp2>>=1;
			step2++;
			vis[temp2]++;
			cnt[temp2]+=step2;
			int temp3=temp2;
			int step3=step2;
			while (temp3<=maxx)
			{
				temp3<<=1;
				step3++;
				vis[temp3]++;
				cnt[temp3]+=step3;
			}

		}
		else
		{
			temp2>>=1;
			step2++;
			vis[temp2]++;
			cnt[temp2]+=step2;
		}
	}

	return 0;
}

int a[maxn];
 
int main()
{  
	int n,i;
	scanf("%d",&n);

	for (i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		maxx=max(maxx,a[i]);
	}
	for (i=1;i<=n;i++)
		slove(a[i]);

	int ans=1<<25;

	for (i=1;i<=2*maxx;i++)
	{
		if (vis[i]==n)
			ans=min(ans,cnt[i]);
	}
	printf("%d\n",ans);
	
	
	
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: