状态压缩+记忆搜索

状态压缩:其实是把一个状态弄成二进制放在dp转移量里,运用位运算操作。

CSP—收集卡牌所涉及的主要是状态压缩与记忆化搜索,有关这方面的题,接触的并不多。记忆化搜索经常伴随着状态压缩,防止TLE,计算过的不用再次计算,当然更快了。

因为数组开成了三维,会出现MLE

#include<bits/stdc++.h>
using namespace std;

const int N = 16, M = 5;

int n, k;

double p[N];

double f[1 << N][N][M * N];    //这个f中的left是可有可无的因为,col集合就表示了还有多少个卡片未抽中 
 //已抽到集合col,还剩下left个卡未抽,目前的硬币数为coins的期望抽卡次数 
double dp(int col, int left,int coins) 
{
	if(f[col][left][coins]>=0)
		return  f[col][left][coins];//记忆化搜索 
	
	if(left * k <= coins) //兑换,无需再进行抽卡 ,返回0, 
		return f[col][left][coins] = 0; 
		
	f[col][left][coins] = 0; 
	
	for(int i = 0; i < n; i ++)
		if(col & (1 << i))//如果抽到的已经有的卡牌,得到硬币
			f[col][left][coins] += p[i] * (dp(col, left, coins + 1) + 1); //不要忘记+1 
		else //抽到没有的卡牌 
			f[col][left][coins] += p[i] * (dp(col | (1 << i), left-1, coins) + 1);//不要忘记+1 
	
	return f[col][left][coins];
}

int main()
{
	ios::sync_with_stdio(false);
	double res = 0;
	//读入 
	cin >> n >> k;
	for (int i = 0; i < n; i ++)
		cin >> p[i];
	
	memset(f, -1, sizeof f);
	
	for(int i = 0; i < n; i ++)
		res += p[i]*(dp(1 << i, n - 1, 0) + 1); //不要忘记+1 
	//输出 
	printf("%.10lf\n",res); 
}

AC的代码如下:

#include<bits/stdc++.h>
using namespace std;

const int N = 16, M = 5;

int n, k;

double p[N];

double f[1 << N][M * N];    //如果不把left去掉会出现MLE,所以去掉left 
 //已抽到集合col,还剩下left个卡未抽,目前的硬币数为coins的期望抽卡次数 
double dp(int col, int left,int coins) 
{
	if(f[col][coins]>=0)
		return  f[col][coins];//记忆化搜索 
	
	if(left * k <= coins) //兑换,无需再进行抽卡 ,返回0, 
		return f[col][coins] = 0; 
		
	f[col][coins] = 0; 
	
	for(int i = 0; i < n; i ++)
		if(col & (1 << i))//如果抽到的已经有的卡牌,得到硬币
			f[col][coins] += p[i] * (dp(col, left, coins + 1) + 1); //不要忘记+1 
		else //抽到没有的卡牌 
			f[col][coins] += p[i] * (dp(col | (1 << i), left-1, coins) + 1);//不要忘记+1 
	
	return f[col][coins];
}

int main()
{
	ios::sync_with_stdio(false);
	double res = 0;
	//读入 
	cin >> n >> k;
	for (int i = 0; i < n; i ++)
		cin >> p[i];
	
	memset(f, -1, sizeof f);
	
	for(int i = 0; i < n; i ++)
		res += p[i]*(dp(1 << i, n - 1, 0) + 1); //不要忘记+1 
	//输出 
	printf("%.10lf\n",res); 
}

洛谷—单词游戏有关这道题,看别人的代码了解了快写,快读(看别人的代码才能学习,但是也太痛苦了)。算法标签为 状态压缩和记忆搜索。很经典的状态压缩+记忆搜索。

#include<bits/stdc++.h>
using namespace std;

const int N = 17;

int f[N][1 << N];//最后一个是第i个word
 
string word[N];

int n;

int dp(int tail, int coll)
{
	if(f[tail][coll])
		return f[tail][coll]; //记忆化搜索
//	f[tail][coll] = 0;  这个也没有必要 
	for(int i = 1; i <= n ; i++)
		if((word[i][0] == word[tail][word[tail].size()-1]) && ! (coll & (1 << (i-1))) )
			f[tail][coll] = max(f[tail][coll], dp(i, coll | (1 << (i-1))) );
	return f[tail][coll] += word[tail].size(); 
}

int main()
{
	int res = 0;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++)
		cin >> word[i];
//	memset(f, -1, sizeof f);其实无必要 
	for(int i = 1; i <= n; i++)
		res = max(res, dp(i, 1 << (i-1)));
	cout << res << endl;
	return 0;
}

洛谷—吃奶酪;这可是我写出的第一个状压+记忆化搜索,庆祝一下。这三道题大同小异。

#include<bits/stdc++.h>
using namespace std;

const int N = 15;

double f[1 << N][N]; //最后经过N,且走过了集合1<< N
int n;
double dist[N][N];
pair<double, double> xy[N];

//计算两点距离 
inline double calDis(pair<double, double> xy1, pair<double, double> xy2)
{
	return sqrt((xy1.first-xy2.first)*(xy1.first-xy2.first) + (xy1.second-xy2.second)*(xy1.second-xy2.second));	
}

double dp(int col, int tail)
{
	
	if(f[col][tail]>=0) 
		return f[col][tail];//记忆化搜索,防止TLE 
	f[col][tail] = 0x3f3f3f3f;
	for(int i = 0; i < n; i ++)
		if(!((1 << i) & col)) //这个点未走过
			f[col][tail] = min(f[col][tail], dp(col | (1 << i), i) + dist[tail][i]);
			
 	f[col][tail]= f[col][tail] == 0x3f3f3f3f? 0:f[col][tail]; //请好好看看该怎么写,被自己无语到 
	return f[col][tail];
}

int main()
{
	double res = 0x3f3f3f3f;
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 0; i < n; i ++)
		cin >> xy[i].first >> xy[i].second;
	//计算距离矩阵 
	for(int i = 0; i < n; i ++)
		for(int j = 0; j < i; j ++)
			dist[j][i] = dist[i][j] = calDis(xy[i], xy[j]);
	memset(f,-1, sizeof f);	//必须的 
	//老鼠从0,0开始	
	//从每个点出发找最小值
	for(int i = 0; i < n; i ++)
		res = min(res, dp(1 << i, i) + calDis(xy[i],make_pair(0,0)));
	//输出 
	printf("%.2lf\n",res);
		
	return 0;	
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值