2019.12.15日常总结

洛谷P2364

【题意】: 麦克正如我们所知的已快乐地结婚,在上个月他胖了 70 70 磅。因为手指上的脂肪过多,使他连给他最亲密的朋友斯拉夫克写一个电子邮件都很困难。

每晚麦克都详细地描述那一天他所吃的所有东西,但有时当他只想按一次某键时往往会按了不止一次,并且他的胖手指还会碰到他不想要按的键,麦克也知道自己的手指有问题,因此他在打字的时候很小心,以确保每打一个想要的字符时误打的字符不超过 3 3 个,误打的字符可能在正确字符之前也可能在其之后。

当斯拉夫克多次收到读不懂的电子邮件后,他总是要求麦克将电子邮件发3遍,使他容易读懂一点。

编写程序,帮助斯拉夫克根据他所收到的三封电子邮件求出麦克可能写出的最长的信。
【思路】: 事实上,“每打一个字符时误打的字符个数不超过 3 3 个”这句话是没有用的。

我们可以从繁杂的题目描述中发现如下事实:本题就是让我们计算最长公共子序列而已,与模板的不同仅仅就是从二维变成了三维。

时间复杂度: O ( O( 三封信的长度的乘积 ) O ( 100 × 100 × 100 ) = O ( 1 × 1 0 6 ) )\leq O(100 \times 100\times 100)=O(1 \times 10^6)
【代码】:

int f[110][110][110];
int p[110][110][110];
char s1[110],s2[110];
char s3[110];int n,m;
int l,l1,l2,l3,len;
char ans[110];
int main(){
	freopen("t1.in","r",stdin);
	scanf("%s\n",s1+1);
	scanf("%s\n",s2+1);
	scanf("%s",s3+1);
	n=strlen(s1+1);
	m=strlen(s2+1);
	l=strlen(s3+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=l;k++){
				if (s1[i]==s2[j]&&s2[j]==s3[k]){
					f[i][j][k]=f[i-1][j-1][k-1]+1;
					p[i][j][k]=1;
				}
				else if (f[i-1][j][k]>=f[i][j-1][k]&&f[i-1][j][k]>=f[i][j][k-1]){
					f[i][j][k]=f[i-1][j][k];
					p[i][j][k]=2;
				}
				else if (f[i][j-1][k]>=f[i-1][j][k]&&f[i][j-1][k]>=f[i][j][k-1]){
					f[i][j][k]=f[i][j-1][k];
					p[i][j][k]=3;
				}
				else{
					f[i][j][k]=f[i][j][k-1];
					p[i][j][k]=4;
				}
			}
//	注意转移方程中的else必不可少!!!
	for(l1=n,l2=m,l3=l;l1&&l2&&l3;){
		if (p[l1][l2][l3]==1){
			ans[++len]=s1[l1];
			l1--;l2--;l3--;
		}
		else if (p[l1][l2][l3]==2) l1--;
		else if (p[l1][l2][l3]==3) l2--;
		else l3--;
	}
//	求答案(注意因为有SPJ,所以我们不必要和样例一模一样)
	for(int i=len;i;i--)
		putchar(ans[i]);
//	注意ans是答案的倒序
	return 0;
}

【备注】: 如果采用如笔者一样的写法,样例输出应为cevaiaauk


洛谷P1385

【题意】: 给定一小写字母串 s s ,每次操作你可以选择一个 p ( 1 p < s ) p(1\leq p<|s|) 执行下述修改中的任意一个:

( 1 ) (1) s p s_p 改为其字典序 + 1 +1 的字母,将 s p + 1 s_{p+1} 改为其字典序 1 -1 的字母

( 2 ) (2) s p s_p 改为其字典序 1 -1 的字母,将 s p + 1 s_{p+1} 改为其字典序 + 1 +1 的字母

在经过任意多次操作后,串 s s 能变化成多少种字符串?

修改过程中必须保证 s s 是合法的小写字母串(即不能对字母a进行字典序 1 -1 的操作),答案对 1000000007 ( 1 0 9 + 7 ) 1000000007(10^9 + 7) 取模。

【思路】: 字典序一加一减,则其总和不变。

既然总和不变,那么我们可以把问题抽象成用az共计 26 26 个物品凑出一个固定的体积。

于是原问题转化为了一个标准的完全背包问题。

【优化前代码】:

const int mod=1e9+7;
#define ll long long
ll f[120][3100];
int num,len,t;
char s[120];
int main(){
	scanf("%d",&t);
	while (t--){
		scanf("\n%s",s+1);
		len=strlen(s+1);num=0;
		for(int i=1;i<=len;i++)
			num=num+(s[i]-'a');
		memset(f,0,sizeof(f));
		for(int i=0;i<26;i++)
			f[1][i]=1;
		for(int i=2;i<=len;i++)
			for(int k=0;k<3001;k++)
				for(int j=0;j<26;j++)
					(f[i][k]+=f[i-1][k-j])%=mod;
		printf("%lld\n",(f[len][num]-1+mod)%mod);
	}
	return 0;
}

【优化】: 我们发现这样做会TLE,为什么呢?

我们发现,我们会重复计算相同的数据(毕竟任何时候同一个 f [ i ] [ j ] f[i][j] 都是一样的)。所以,我们可以提前计算出所有的 f f ,需要时直接调用即可。

其实际上,这是一种很有用的算法:答案提前计算

【优化后代码】:

const int mod=1e9+7;
#define ll long long
ll f[120][3100];
int num,len,t;
char s[120];
int main(){
	freopen("t1.in","r",stdin);
	memset(f,0,sizeof(f));
	for(int i=0;i<26;i++)
		f[1][i]=1;
	for(int i=2;i<101;i++)
		for(int k=0;k<3001;k++)
			for(int j=0;j<26;j++)
				(f[i][k]+=f[i-1][k-j])%=mod;
	scanf("%d",&t);
	while (t--){
		scanf("\n%s",s+1);
		len=strlen(s+1);num=0;
		for(int i=1;i<=len;i++)
			num=num+(s[i]-'a');
		printf("%lld\n",(f[len][num]-1+mod)%mod);
	}
	return 0;
}

洛谷P4408

【题意】: Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:“喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?”一听说要考试,Chris的父母就心急如焚,他们决定在尽量短的时间内找到Chris。他们告诉Chris的老师:“根据以往的经验,Chris现在必然躲在朋友ShermieYashiro家里偷玩《拳皇》游戏。现在,我们就从家出发去找Chris,一但找到,我们立刻给您打电话。”说完砰的一声把电话挂了。

Chris居住的城市由 N N 个居住点和若干条连接居住点的双向街道组成,经过街道 x x 需花费 T x T_x 分钟。可以保证,任两个居住点间有且仅有一条通路。Chris家在点 C C ShermieYashiro分别住在点 A A 和点 B B Chris的老师和Chris的父母都有城市地图,但Chris的父母知道点 A A B B C C 的具体位置而Chris的老师不知。

为了尽快找到ChrisChris的父母会遵守以下两条规则:

( 1 ) (1) 如果 A A 距离 C C B B 距离 C C 近,那么Chris的父母先去Shermie家寻找Chris,如果找不到,Chris的父母再去Yashiro家;反之亦然。
( 2 ) (2) Chris的父母总沿着两点间唯一的通路行走。
显然,Chris的老师知道Chris的父母在寻找Chris的过程中会遵守以上两条规则,但由于他并不知道 A B C A,B,C 的具体位置,所以现在他希望你告诉他,最坏情况下Chris的父母要耗费多长时间才能找到Chris

【思路】: 由规则 2 2 可知,本图是一棵树。

我们先求出这棵树的直径 ( a , b ) (a,b) ,然后跑两遍最短路算法,分别求出每个点到 a , b a,b 的最短路的长度(分别记为 d 1 , d 2 d_1,d_2 ),答案就是 d 1 [ b ] + m a x { d 1 [ i ] + d 2 [ i ] } d_1[b]+max\{d_1[i]+d_2[i]\}
【代码】:

#define ll long long
const int N=2e5+1e3;
struct node{
	int next,to,len;
}e[N<<1];int h[N],tot;
inline void add(int a,int b,int c){
	e[++tot]=(node){h[a],b,c};h[a]=tot;
}
ll d1[N],d2[N];bool vis[N];
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
int n,m,u,v,len;ll ans;
inline void dijkstra(ll d[],int s){
	memset(vis,true,sizeof(vis));
	for(int i=1;i<=n;i++)
		d[i]=1e18;//注意这里不能写成memset(d,127,sizeof(d));
//	printf("d[1]=%lld\n",d[1]);
	d[s]=0;vis[s]=true;
	priority_queue<pair<ll,int> >q;
	q.push(make_pair(0,s));
	while (q.size()){
		pair<int,int> z=q.top();q.pop();
		register int u=z.second;
		if (!vis[u]) continue;
		vis[u]=false;
		for(int i=h[u];i;i=e[i].next){
			register int to=e[i].to;
			if (d[to]>d[u]+e[i].len){
				d[to]=d[u]+e[i].len;
				q.push(make_pair(-d[to],to));
			}
		}
	}
}//dijkstra求最短路
int farthest_node(int s){
	dijkstra(d2,s);
	register ll p=0,len=0;
	for(int i=1;i<=n;i++)
		if (d2[i]>len){
			len=d2[i];
			p=i;
		}
	return p;
}//求离s最远的点
ll calc_answer(){
	int a=farthest_node(1);
	int b=farthest_node(a);
//	(a,b)-->树的直径
	memcpy(d1,d2,sizeof(d2));
	dijkstra(d2,b);
	register ll ans=0ll;
	for(int i=1;i<=n;i++)
		ans=max(ans,min(d1[i],d2[i]));
	return ans+d1[b]; 
}
int main(){
	freopen("t1.in","r",stdin);
	n=read();m=read();
	for(int i=1;i<=m;i++){
		u=read();v=read();len=read();
		add(u,v,len);add(v,u,len);
	}
	ans=calc_answer();
	cout<<ans;
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1775

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/103547925