TOPCODER SRM 612 DIV2

250:统计串中‘RL’的数目即可。

#include <string>

class LeftAndRightHandedDiv2
{
public:
	int count(string S)
	{
		int n = 0;
		for(int i = 0; i < S.length - 1; i++)
		{
			if(S[i] == 'R' && S[i + 1] == 'L')
				n++;
		}
		return n;
	}

	/* data */
};

500:每次要么做复制操作,要么做粘贴操作。

第一种解法:枚举每次是复制,还是粘贴,搜索所有的情况。每次搜索需记录下当前所花的步数、已打出的笑脸的个数、复制板上笑脸的个数。

#include "stdio.h"
#include <memory.h>

//dfs
class EmotionDiv2
{
public:
	int printSmiles(int smiles)
	{
		s = smiles;
		result = 10000;
		dfs(0, 1, 0);
		return result;

	}

private:
	void dfs(int step, int count, int copy)
	{
		if(count > s)
			return;
		if(count == s)
		{
			if(step < result)
				result = step;
		//	printf("%d\n", result);
			return;
		}
		if(copy < count)
		{
			dfs(step + 1, count, count);	//copy
		}
		if(copy != 0)
		{
			dfs(step + 1, count + copy, copy);		//paste
		}

	}

private:
	int result;
	int s;

	/* data */
};
第二种解法:这是一个每步做出选择的最优化问题,可考虑使用dp求解。

定义dp[i][j],表示打出i个笑脸,复制板上有j个笑脸时所需的最小步数。初始情况dp[1][0] = 0。由于只有copy、paste两种操作,因此到达dp[i][j]状态要么是采用了copy操作,要么是paste操作。(1)paste操作,dp[i][j] 只能从dp[i-j][j] + 1转移而来,因为复制板上笑脸个数是j个。(2)copy操作,在这种情况下,必有i == j,且此状态从dp[i][k] + 1转移而来,k <= i。

class EmotionDiv2
{
public:
	int printSmiles(int smiles)
	{
		init();
		printf("hello\n");
		dp[1][0] = 0;
		for(int i = 1; i <= smiles; i++)
		{
			for(int j = 1; j <= i; j++)
			{

				if(dp[i - j][j] + 1 < dp[i][j])
				{
					dp[i][j] = dp[i - j][j] + 1;				//paste
					printf("%d %d:%d\n", i, j, dp[i][j]);

				}
				if(i == j)
				{
					for(int k = 0; k <= i; k++)
					{
						if(dp[i][k] + 1 < dp[i][j])
						{
							dp[i][j] = dp[i][k] + 1;			//copy
							printf("%d %d:%d\n", i, j, dp[i][j]);
						}
					}
				}
			}
		}
		int result = 10000;
		for(int i = 0; i <= smiles; i++)
		{
			if(dp[smiles][i] < result)
				result = dp[smiles][i];
		}
		return result;
	}

private:
	void init()
	{
		for(int i = 0; i < 1010; i++)
		{
			for(int j = 0; j < 1010; j++)
			{
				dp[i][j] = 10000;
			}
		}
	}

	/* data */
private:
	short dp[1010][1010];
};
1000:先统计对于不同的i,2 i 的数有多少个。然后最开始我写了一个暴搜的方法,即对于每一个i枚举选择的个数,这个方法复杂度较高,对大数据超时了。注意到每个数都是2的幂,因此如果在组成某一个和的过程中,选择了k个2 i ,k是偶数,那么它等价于选择了k/2个2 i+1 ,比方说数据集合是{2,2,2,4},那么他们组成的和的个数与{2,4,4}的个数是等价的。因此对于2 i ,有两种选择策略:(1)如果选择偶数个(k),则等价于选择了k/2个2 i+1 。(2)如果选择了奇数个(k),则等价于选择了1个2 i ,(k-1)/2个2 i+1

而选择偶数个2i 造成的结果是所得到的和的二进制串的第i位是0,选择奇数个则是该位是1。因此本问题可以对得到的和的每一位进行枚举:对于第i位,如果有K个2i ,如果该位是0,那么可以转化为k/2个2i+1 。如果该位是1,那么可以保留1个2i ,得到(k-1)/2个2i+1 。那么假如枚举到第k位,那么k位以下的那些位都是已经确定的,因此若2k  的数目是0,那么向上转化的过程就结束了,返回1即可,因为这和的每一位都已经确定了。得到如下递归程序。

#include <vector>
#include <map>
#include <set>
#include <stdio.h>
#include <iostream>
#include <memory.h>
#include <algorithm>
using namespace std;

#define MAX_POWER 50
#define MAX_CNT 50

class PowersOfTwo
{
public:
	PowersOfTwo(){}
	~PowersOfTwo(){}

	long long count(vector<long long> powers)
	{
		for(unsigned int i = 0; i <= MAX_POWER; i++)
		{
			v[i] = std::count(powers.begin(), powers.end(), 1ll << i);
			// cout << i << " " << v[i] << ",";
		}
		memset(cnt, -1, sizeof(cnt));
		return f(0, 0);
	}
	


private:
	long long f(int k, int b)
	{
		if(k == MAX_POWER + 10)
			return 1;
		long long res = cnt[k][b];
		if(res == -1)
		{
			res = 0;
			int x_k = v[k] + b;
			res += f(k + 1, x_k / 2);			//even
			if(x_k > 0)
				res += f(k + 1, (x_k - 1) / 2);		//odd
			cnt[k][b] = res;
		}
		return res;
	}

private:
	int v[MAX_POWER + 10];
	long long cnt[MAX_POWER + 10][MAX_CNT];

	/* data */
};


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值