2019.07.08【NOIP提高组】模拟 A 组

本文深入解析了三道算法竞赛题目,分别介绍了倒推思想在优化复杂度中的应用、BFS在图像处理中的巧妙运用,以及线头DP在解决特定序列问题上的独特方法。通过实例分析,展示了算法设计与优化的关键技巧。

T1:这题不难,但要发现一些性质。

首先我们设f[i]表示n=i时的答案,那么不难推出可以由f[i-i%p[j]]转移到f[i],但是这个转移的复杂度是O(nm)的,需要优化。

我们考虑倒推:既然n能推到n-n%p[i],那么我们也可以用k推到k+r(注意要满足p[i]|k且1<=r<=p[i])。

进而我们可以发现:设p0为整除k的且在给出范围中的最大素数,那么k能推到的范围就是k+1~k+p0-1,即可以用f[k]去更新f[k+1~k+p0-1]。

由此我们就可以证明f的单调不递减性。因为如果一个f[j]能更新f[i+1],那么f[j]必能更新f[i],所以f[i]<=f[i+1],得证。

再进一步,我们发现f是一个分段函数,且相邻段之间的差为1。这也就是说f从1开始肯定是一段1、一段2、一段3……

既然这样,那么我们考虑用当前段的端点l、r来求出下一段的端点l1、r1。首先显然l1=r+1,而r1=max(i+p0-1),(l<=i<=r),即r1为l~r能更新到的最远点。

但是我们如果直接枚举i进行质因数分解的话会超时,所以我们可以枚举给出的素数,然后通过计算出l~r中最大的素数的倍数来求r1。

 

T2:正解很简单,但是很巧妙。

首先从左上角开始bfs,求出与左上角颜色相同的联通块,然后把联通块反色,在做一边。每次bfs时ans加1,一直bfs到所有有颜色的点都被遍历到为止。

证明很好想,这里就不多讲了。

 

总结:这一题主要运用到倒推的思想(包括第一题也是)。以后遇到这种十分棘手题可以多考虑一下如何从最后的状态往前推。

 

T3:神奇的线头dp。

我们先转化一下题目:既然每一个e都要话2的代价去删除它,那么我们不妨把e全从原序列中去除,然后把每一个e后面的点设为必经点。而线头dp就是说在一个数轴中要连线使路径进过一些必经点(太难表达了)。

对于这一道题,设f[i][j]表示第i个点前有一条线直接从它的前面连到后面一个字母为j的位置去的最小代价。

g[i][j]表示第i个点前面有一条直线连到i的后面一个字母为j的位置去,然后从那个位置走到i前面某一个,再从那个“某一个位置”连到一个i后面的字母为k的位置上去的最小代价。

转移时我们要注意分类讨论j是否等于a[i]的情况,并且在求f[i][j]是如果i是必经点的话就不能直接把i给跳了。

至于初始值,就是f[0][a[1]]=0。而答案就是f[n][c]=1,c为一个不存在的字母(可设为11).这样求答案的意思是最后把线连向某个处于最后位置的不存在的字母,这样做是为了方便求答案。最终ans=f[n][c]+se*2-2。se*2表示的是“每一个e都要话2的代价去删除它”,-2为的是减去连向不存在的字母的代价。

详细解释看https://www.cnblogs.com/Itst/p/10339605.html

最后贴一下代码(转移方程有10条,十分复杂):

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAXN 70010
#define MAXV 12
using namespace std;

int a[MAXN],bz[MAXN],f[MAXN][MAXV],g[MAXN][MAXV][MAXV],n,se;
char a1[MAXN];
int main()
{
int i,j,k,s;
scanf("%d\n%s",&n,a1);
for(i=1;i<=n;i++)a[i]=a1[i-1]-'a'+1;
for(i=1;i<=n;i++)
	if(a[i-1]==5)bz[i]=1;
s=0;
for(i=1;i<=n;i++)
	if(a[i]!=5){s++;a[s]=a[i];bz[s]=bz[i];}
se=n-s;n=s;
n++;a[n]=11;bz[n]=bz[n-1];
memset(f,0x7f,sizeof(f));
memset(g,0x7f,sizeof(g));
f[0][a[1]]=0;
for(i=1;i<=n;i++)
	for(j=1;j<=11;j++)
	{
		if(j!=a[i]&&bz[i]==0)f[i][j]=min(f[i][j],f[i-1][j]);
		f[i][j]=min(f[i][j],f[i-1][a[i]]+2);
		if(j!=a[i])f[i][j]=min(f[i][j],g[i-1][a[i]][j]);
		f[i][j]=min(f[i][j],g[i-1][a[i]][a[i]]+2);
		for(k=1;k<=11;k++)
		{
			if(j!=a[i])g[i][j][k]=min(g[i][j][k],f[i-1][j]+3);
			g[i][j][k]=min(g[i][j][k],f[i-1][a[i]]+5);
			if(j!=a[i]&&k!=a[i])g[i][j][k]=min(g[i][j][k],g[i-1][j][k]+1);
			if(k!=a[i])g[i][j][k]=min(g[i][j][k],g[i-1][a[i]][k]+3);
			if(j!=a[i])g[i][j][k]=min(g[i][j][k],g[i-1][j][a[i]]+3);
			g[i][j][k]=min(g[i][j][k],g[i-1][a[i]][a[i]]+5);
		}
	}
printf("%d",f[n-1][11]+se*2-2);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值