2021年5月9日普转提测试总结

2021年5月9日普转提测试总结

前言

打比赛时总有一种故意出一套简单题让我们考,然后考不好就会被制裁的感觉…

考试情况

读完T1就有了很明确的思路,类似于前几天打的ABC中的一道题,将所有数字被分成组,对每一组进行处理。观察数据范围,第一档分数暗示性极强的状态压缩,且分值极高,正解也有一定的想法,于是二十分钟码出来了T1
T2读题时没读懂,没读懂的原因是我觉得这个题的难度不太正常…读了好几遍感觉都是那个意思,就顺着想下去了。去厕所时想到当每一位存在1,1,0时,就可以确定出符号具体为什么,回去简单码了一下,就下一题了
受到前两题的影响,我已经觉得这个比赛难度不太正常了,于是就以为T3也很简单,却怎么也想不出正解,果断跳过看了T4
T4的暴力分40分是很裸的,但正解没想出来,就先把暴力码了出来
剩下T3和T4的正解,还有两个小时比赛才结束…
想T3存在哪些性质,发现每个数字最大只能变为60,否则它的性价比就不如变为1,所以,五重循环暴力是能把三十分搞出来的,然后…然后就没有然后了,也想到了一些其他性质,例如什么情况下所有数字才都互质,但是并不会实现。
总体来说能拿的分数算是都拿了,没有因为细节而疯狂掉分…
改变了一下做题风格吧,没有把题全部审完再去码,主要原因是前面题太简单了让我不得不码

题解

T3

观察数据范围, n n n a i a_i ai的数据范围都极小,考虑在此基础上做文章
n n n的数据范围小,其实就有了大致的做题方法:搜索|状压
但其实正解也就是状压,暴力也就是通过搜索来解决的
观察 a i a_i ai的数据范围,去想整个数组的性质,发现 a i a_i ai最多变到58,否则性价比还不如直接变为1.
这些东西考试时候确确实实都想到了,但像状压这类的算法还是掌握的不够熟练,故无法写出正解
题解如下(不自己写题解就是因为这个题解写的太好了!):
题解
配合上代码就非常好理解了!

code
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=110;
int a[N],b[N],dis[65],p[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int tt[N]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,37,41,43,47,49,53};
int tot=38;
vector<int> can[1<<16];
//a: 原数组
//b: 最终答案 
//dis:每个数质因数的状态
//p:60以内的质数 
int f[110][1<<16];
int main()
{
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=58;i++)//预处理出1-58质因子状态 
		for(int j=0;j<16;j++)
			if(i%p[j]==0)
				dis[i]|=(1<<j);	
	for(int i=0;i<(1<<16);i++)//优化2
		for(int j=1;j<=tot;j++)
			if((i|dis[tt[j]])==i) 
				can[i].push_back(j);
	memset(f,0x3f,sizeof(f));//初始化 
	f[0][0]=0; 
	for(int i=1;i<=n;i++)
		for(int j=0;j<(1<<16);j++)
			for(int k=0;k<can[j].size();k++)
				f[i][j]=min(f[i][j],f[i-1][j^dis[tt[can[j][k]]]]+abs(a[i]-tt[can[j][k]]));//状态转移 
	//回头找答案 
	int ans=1e9,t;//t记录使结果最优的最终状态 
	for(int i=0;i<(1<<16);i++)
		if(ans>f[n][i])
		{
			ans=f[n][i];
			t=i;
		}
	for(int i=n;i>=1;i--)
		for(int j=1;j<=tot;j++)
		{
			if((t|dis[tt[j]])!=t) continue;
			if(f[i-1][t^dis[tt[j]]]+abs(a[i]-tt[j])==f[i][t])
			{
				t^=dis[tt[j]];
				b[i]=tt[j];
				break;
			}
		}
	for(int i=1;i<=n;i++) printf("%d ",b[i]);
	return 0;
 } 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值