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;
}