D. Digits 乘法用log转换加法 + DP 区域赛原题

原题链接

不得不说区域赛的题目,考察的确实是综合能力
这个题开始没想到log的方法,就去想循环节暴搜了。但是循环节暴搜有一个问题就是,每次找到方案后都要重新计算一次乘积,毕竟是暴搜,能找到很多可行方案,TLE了。
如果不用log将乘法转换为加法,这题是没法dp的,因此重要的处理前提就是想到取log。
不得不说,如果是求使用的数的个数最多,循环节暴搜还是很好的。但是这题求的是乘积的最大值,这就搞不了了。
题意:给你一堆数,问你用哪些数能乘起来能得到最大的,个位是d的数。

思路:DP, f[i][j]表示当前选到第i个数,个位数是j的乘积的最大值,此时已经转化为log的和的最大值了。

代码如下

struct node{
	bool use;	//表示当前这个数有没有被使用 
	int x, y;	//当前数是从 f[x][y]转移过来的 
};
double f[N][10];
node pre[N][10];
double lg[N];
int a[N], n, d;

int main()
{
	IOS;
	cin >> n >> d;
	for(int i = 1 ; i <= n ; i ++)
	{
		cin >> a[i];
		lg[i] = log(a[i]);
	}
	
	for(int i = 1 ; i <= n ; i ++)
	{
		int c = a[i] % 10;
		f[i][c] = lg[i];
		pre[i][c] = {true, i, c};		//自己单独当然可以 
		for(int j = 0 ; j < 10 ; j ++)
		{
			if(f[i][j] < f[i - 1][j])	//不选当前这个数 
			{
				f[i][j] = f[i - 1][j];
				pre[i][j] = {false, i - 1, j};
			}
			for(int k = 0 ; k < 10 ; k ++)	//选当前这个数 
			{
				if(k * c % 10 == j && f[i - 1][k])
				{
					if(f[i][j] < f[i - 1][k] + lg[i])
					{
						f[i][j] = f[i - 1][k] + lg[i];
						pre[i][j] = {true, i - 1, k};
					}
				}
			}			
		}
	}
	
	double maxd = 0;
	int idx = 0;
	for(int i = 1 ; i <= n ; i ++)
		if(maxd < f[i][d])
		{
			maxd = f[i][d];
			idx = i;
		}
	if(!maxd)
	{
		cout << -1;
		return 0;
	}
	vector<int> ans;
	auto t = pre[idx][d];
	if(t.use) ans.push_back(a[idx]);
	while(t.x != idx)
	{
		idx = t.x;
		d = t.y;
		t = pre[t.x][t.y];
		if(t.use) ans.push_back(a[idx]);
		if(t.x == idx) break; 
	}
	
	cout << ans.size() << endl;
	for(auto x : ans) cout << x << " ";
	

	return 0;
}
posted @ 2021-04-05 18:32  beatlesss  阅读(271)  评论(0编辑  收藏  举报