17周总结

记忆化深搜求最短路+路径

 1:兔子与樱花

总时间限制: 1000ms

内存限制: 65535kB

描述

很久很久之前,森林里住着一群兔子。有一天,兔子们希望去赏樱花,但当他们到了上野公园门口却忘记了带地图。现在兔子们想求助于你来帮他们找到公园里的最短路。

输入

输入分为三个部分。
第一个部分有P+1行(P<30),第一行为一个整数P,之后的P行表示上野公园的地点。
第二个部分有Q+1行(Q<50),第一行为一个整数Q,之后的Q行每行分别为两个字符串与一个整数,表示这两点有直线的道路,并显示二者之间的矩离(单位为米)。
第三个部分有R+1行(R<20),第一行为一个整数R,之后的R行每行为两个字符串,表示需要求的路线。

输出

输出有R行,分别表示每个路线最短的走法。其中两个点之间,用->(矩离)->相隔。

样例输入

6
Ginza
Sensouji
Shinjukugyoen
Uenokouen
Yoyogikouen
Meijishinguu
6
Ginza Sensouji 80
Shinjukugyoen Sensouji 40
Ginza Uenokouen 35
Uenokouen Shinjukugyoen 85
Sensouji Meijishinguu 60
Meijishinguu Yoyogikouen 35
2
Uenokouen Yoyogikouen
Meijishinguu Meijishinguu

样例输出

Uenokouen->(35)->Ginza->(80)->Sensouji->(60)->Meijishinguu->(35)->Yoyogikouen
Meijishinguu

这是一道求任意两点最短路径+打印路径的题,由于本题数据小,所以我用记忆化深搜加回溯做的,代码如下

#include<bits/stdc++.h>
using namespace std;
map<string,int> mp1;
map<int,string> mp2;
int t,n,m,a,x,y,ans=9999999,w=-1;
string s1,s2;
int s[35][35]= {0},u[35]= {0};
int d[35],p[35];//memset(s,0,sizeof(s));//d为最终求得的最短路,p记录搜索过程中的路径
void Dfs(int x,int h,int num) {
	if(x==y) {
		if(num<ans) {//更新路径
			for(int i=0; i<h; i++)
				d[i]=p[i];
			w=h;
			ans=num;
			return ;
		}
	}
	for(int i=1; i<=t; i++) {
		if(s[x][i]!=0&&num+s[x][i]<ans&&u[i]==0) {//num+s[x][i]<ans,枝剪
			p[h++]=i;
			u[i]=1;
			num+=s[x][i];
			Dfs(i,h,num);
			h--;
			u[i]=0;
			num-=s[x][i];//回溯
		}
	}
}
int main() {
	cin >> t;
	for(int i=1; i<=t; i++) {
		cin >> s1;
		mp1[s1]=i;
		mp2[i]=s1;
	}
	cin >> n;
	while(n--) {
		cin >> s1 >> s2 >> a;
		s[mp1[s1]][mp1[s2]]=a;
		s[mp1[s2]][mp1[s1]]=a;
	}
	cin >> m;
	while(m--) {
		ans=9999999,w=-1;
		cin >> s1 >> s2;
		x=mp1[s1],y=mp1[s2];
		memset(d,0,sizeof(d));
		memset(p,0,sizeof(p));
		memset(u,0,sizeof(u));
//		for(int i=1;i<=t;i++){
//			for(int j=1;j<=t;j++)
//			cout << s[i][j] << " ";
//			cout << endl;
//		}
		Dfs(x,0,0);
		cout << s1;
		for(int i=0; i<w; i++) {
			if(i==0)
				cout << "->(" << s[x][d[i]] << ")->" << mp2[d[i]];
			else
				cout << "->(" << s[d[i-1]][d[i]] << ")->" << mp2[d[i]];
		}
		cout << endl;
	}
	return 0;
}

其实本题可以用佛洛伊德算法更好的解决,时间复杂度更低,可以参考以下代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
 
int n,m,k;
string a[66];
int step[66][66],g[66][66];
map<string,int>mp;
const int inf=0x3f3f3f3f;
int s,e;
 
void init()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			g[i][j]=(i==j?0:inf);
			step[i][j]=j;
		}
	}
 } 
 void floyd()
 {
 	for(int i=1;i<=n;i++)
 	{
 		for(int j=1;j<=n;j++)
		 {
		 	for(int k=1;k<=n;k++)
		 	{
		 		if(g[j][k]>g[j][i]+g[i][k])
		 		{
		 			g[j][k]=g[j][i]+g[i][k];
		 			step[j][k]=step[j][i];
				 }
			 }
		  } 
	 }
 }
void display()
{
	cout<<a[s];
	int k=s;
	while(k!=e)
	{
		cout<<"->("<<g[k][step[k][e]]<<")->"<<a[step[k][e]];
		k=step[k][e];
	}
	cout<<endl;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		mp[a[i]]=i;
	}
	init();
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		string s1,s2;int d;
		cin>>s1>>s2>>d;
		g[mp[s1]][mp[s2]]=g[mp[s2]][mp[s1]]=d;
	 } 
	floyd();
	cin>>k;
	while(k--)
	{
		string s1,s2;cin>>s1>>s2;
		s=mp[s1];e=mp[s2];
		display();
	}
	
	return 0;
}

最小生成树问题

牛客:喜迎暑假多校联赛第一场H题:呜米喵想要成为爱抖露!

链接:https://ac.nowcoder.com/acm/problem/222737
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

这一天MeUmy的大草原上突然出现了一个anti,他用了一个奇怪的装置把呜米的直播账号给锁住了。并留下说明书后光速跑路!

解锁账号有两种方法!

一、

这个装置上有一个地图,上面有n座城市。还有一张数据表,表内有m条数据,表示了每个城市之前存在的交通路线,格式为三个整数A B C  代表A城市到B城市需要花费C元。 (路是双向的 出题人没见过单向的路....)

呜米需要用这些连接方式把所有城市连在一起,连接城市需要用到的交通路线的总花费小于K元,才能解锁自己的账号。

二、

只要呜米去翻垃圾桶,然后喝长毛的的奶茶。账号就会自动解锁!!!

因为呜米不想用第二方法,所以她把城市进行了编号1-n,然后把交通路线的数据发到了群里。求救!

这时一位gachi看不下去了,他对这个装置进行了改造,但是他比那个anti弱所以没办法直接解开,不过在他的不懈努力之下这个装置终于被他搞出了BUG。

本来这个装置在呜米连接好所有城市后会计算总花费。但因为那个bug导致这个装置,会把费用是质数的边当做费用为0。

比如A B C,代表A城市到B城市需要花费C元,C如果是质数,在用的时候费用算作0。

所以第一个方法完成的难度降低了。

输入描述:

第一行三个整数 n m k

1≤n≤m≤2∗1051\leq n \leq m \leq 2*10^{5}1≤n≤m≤2∗105 1≤k≤1091\leq k \leq 10^{9}1≤k≤109

以下m行每行三个整数 A B C

1≤A≤B≤n1\leq A\leq B\leq n1≤A≤B≤n   1≤C≤1071\leq C \leq 10^{7}1≤C≤107

输出描述:

如果第一种方法可以解锁账号 请输出:wmmxycwdjdwdlnljbzwtskirakira

本来应该输出这个但是qcjj说中文会炸(呜米喵想要成为大家的爱抖露 努力进步在舞台上kirakira)

如果需要第二种方法解锁账号 请输出:wmmxycwdjdwdlnljbzwtsfljt

本来应该输出这个但是qcjj说中文会炸(呜米喵想要成为大家的爱抖露 努力进步在舞台上翻垃圾桶)

输入

5 2 8
2 2 38
4 5 16

输出

wmmxycwdjdwdlnljbzwtsfljt

一道求最小生成树的题,只需对边进行排序,然后加边(这里要判断加的边是否连接两个独立的块),这里我用并查集进行判断,详情看代码

#include<bits/stdc++.h>
using namespace std;
bool p[10000005];
struct xxx {
	int x,y,num;
} s[200005];
int f[200005];
bool cmp(xxx a,xxx b) {
	return a.num<b.num;
}
int find(int x) {
	return f[x]==x?x:f[x]=find(f[x]);//压缩路径f[x]=find(f[x])
}
void unit(int x,int y) {
	int x1=find(f[x]),y1=find(f[y]);
	f[x1]=y1;//合并根节点
}
int main() {
	long long n,m,k,b=0,ans=0;
//素数
	for(int i=1; i<=10000001; i++)
		p[i]=true;
	for(int i=2; i<=10000001; i++) {
		for(int j=i+i; j<=10000001; j+=i) {
			p[j]=false;
		}
	}
	cin >> n >> m >> k;
	for(int i=1; i<=n; i++)
		f[i]=i;
	for(int i=0; i<m; i++) {
		scanf("%d %d %d",&s[i].x,&s[i].y,&s[i].num);
		if(p[s[i].num])
			s[i].num=0;
	}
	sort(s,s+m,cmp);
	for(int i=0;i<m;i++){
		if(b==n-1)
			break;
		int x=find(s[i].x),y=find(s[i].y);
		if(x!=y) {
			b++;
			ans+=s[i].num;
			unit(x,y);
		}
	}
//	for(int i=0;i<m;i++)
//	cout << s[i].x << " "<< s[i].y << " " << s[i].num << endl;
//	cout << b << endl << ans << endl;
	if(b==n-1&&ans<k)
		cout << "wmmxycwdjdwdlnljbzwtskirakira" << endl;
	else
		cout << "wmmxycwdjdwdlnljbzwtsfljt" << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值