SRM 605 D2 L3: AlienAndSetDiv2,DP

题目来源:http://community.topcoder.com/stat?c=problem_statement&pm=12954

参考:http://apps.topcoder.com/wiki/display/tc/SRM+605


这题目有点难,关键是dp状态的选择不好想,在实现上用了 C++ STL中的set 和 map,效率不是很高,可以考虑用 bitmask 提高效率。

代码如下:

#include <algorithm>
#include <iostream>
#include <sstream>

#include <string>
#include <vector>
#include <stack>
#include <deque>
#include <queue>
#include <set>
#include <map>

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <cstring>

using namespace std;


/*************** Program Begin **********************/
typedef set <int> seti;
const int MOD = 1000000007;

class AlienAndSetDiv2 {
public:
	int N, K;
	map <seti, int> dp[105];
	int calc(int n, seti unmatched)
	{
		int res = 0;
		map <seti, int>::iterator it;
		it = dp[n].find(unmatched);
		if (it != dp[n].end()) {
			return dp[n][unmatched] % MOD;
		}

		if (n == 2 * N + 1) {
			if (unmatched.size() == 0) {
				res = 1;
			}
		} else {
			seti newset;
			if (unmatched.size() == 0) {
				newset = unmatched;
				newset.insert(n);
				res += ( 2 * calc(n+1, newset) ) % MOD;
			} else {
				newset = unmatched;
				int m = *newset.begin();
				newset.erase(newset.begin());
				res += calc(n+1, newset);
				res %= MOD;

				if (m != n - K) {
					newset = unmatched;
					newset.insert(n);
					res += calc(n+1, newset);
					res %= MOD;
				}
			}	
		}
		dp[n][unmatched] = res;
		return res;
	}

	int getNumber(int N, int K) {
		this->N = N;
		this->K = K;
		int res = 0;
		seti unmatched;
		res = calc(1, unmatched);
		return res;
	}

};

/************** Program End ************************/


使用 bitmask 方法优化,代替 set。最坏情况由原来的220ms 提高到了6ms。

注意:位操作符的优先级要注意,很容易出错,最好都加上括号。

#include <algorithm>
#include <iostream>
#include <sstream>

#include <string>
#include <vector>
#include <stack>
#include <deque>
#include <queue>
#include <set>
#include <map>

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <cstring>

using namespace std;


/*************** Program Begin **********************/
const int MOD = 1000000007;
const int MAX_N = 50;
const int MAX_K = 10;
int dp[2 * MAX_N + 2][1 << MAX_K];

class AlienAndSetDiv2 {
public:
	int N, K;
	int calc(int n, int unmatched)
	{
		int res = 0;
		if (-1 != dp[n][unmatched]) {
			return dp[n][unmatched];
		}

		if (n == 2 * N + 1) {
			if (0 == unmatched) {	// 匹配成功
				res = 1;
			}
		} else {
			if (0 == unmatched) {
				res += ( 2 * calc(n + 1, 1) ) % MOD;
			} else {
				int newset = unmatched;
				int i = 0;
				for (i = 0; (newset & 0x80000000 ) == 0; newset = ( newset << 1 ), ++i) {
					// 注意位操作的优先级,要加括号
				}
				int mx = 31 - i;	// mx 为unmatched不为0的最高位
				// match with largest:
				res += calc(n + 1,  (unmatched - (1 << mx)) << 1 );	// 注意操作符优先级,要加括号
				res %= MOD;

				if (mx != K - 1) {
					newset = unmatched;
					newset = ( (newset << 1) | 1 );
					res += calc(n + 1, newset);
					res %= MOD;
				}
			}	
		}
		dp[n][unmatched] = res;
		return res;
	}

	int getNumber(int N, int K) {
		this->N = N;
		this->K = K;
		int res = 0;
		memset(dp, -1, sizeof(dp));
		res = calc(1, 0);
		return res;
	}
};

/************** Program End ************************/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值