记录week7的实验一道题+作业三道题

路径解析、TT 的魔法猫、TT 的旅行日记、TT 的美梦

作业:

TT 的魔法猫:
在这里插入图片描述

这道题是用的Floyd算法解决,Floyd算法主要用在
1、求多源最短路径
2、求传递闭包
因为胜负具有传递性,所以是一个传递闭包问题。目的是求有多少个场胜负无法预测,于是将插点求最短路的判断改为插点求&运算。即

map[i,j] = Max { map[i,k]&map[k,j] , map[i,j] };

Floyd算法空间复杂度O(n^2)。
时间时间复杂度为O(n^2)。
在初始化时建立邻接矩阵。这题空间复杂度是没问题的。但是这里由于Floyd算法复杂度为:O(n^3),所以要进行剪枝,观察发现因为是&运算,如果有一个是0,结果为0。也就是说如果map[i,k]=0;就不再枚举j。这样就不会TLE。

#include<bits/stdc++.h>
using namespace std;
#define maxn 505 
int N;
int n,m;
int G[maxn][maxn];
int ans;


int main(){
	scanf("%d",&N);
while(N--)
{
    scanf("%d%d",&n,&m);
    memset(G,0,sizeof(G));
	for(int i=0;i<m;i++)
	{   int a=0,b=0;
		scanf("%d%d",&a,&b);
		G[a][b]=1;//只需要输入一条边 总边=胜者+败者+无法预测*2
		 } 	
    for(int k=1;k<=n;k++)
    {
    	for(int i=1;i<=n;i++)
    	{
    		if(G[i][k]==1)//剪枝 这里为0 结果不可能为1了 
    		for(int j=1;j<=n;j++)
    		{
    			if(G[k][j]==1)G[i][j]=1;
			}
		}
	}
   for(int i=1;i<=n;i++)
   for(int j=1;j<=n;j++)
   if(G[i][j]==1)ans++;
   ans=n*(n-1)/2-ans;
   printf("%d\n",ans);
   ans=0;
		
}
	
	return 0;
} 

TT 的旅行日记:
在这里插入图片描述
这道题也是一个求最短路径问题。但是这道题不能直接用Dijkstra或者SPFA求。他有个问题是怎么处理商业线。因为在最短路中只能用一次商业线。所以将求起点到终点的最短路改为 :遍历所有商业线,求起点到商业线的一个点的最短路+终点到商业线的另一个点的最短路。再与起点到终点取最小值即可。
这里用堆优化的Dijkstra来做,建立最大堆,入堆的时候入距离的相反数。这样最大值变成了最小值,弹出的最大值其实就是最小值。这样就等于初始了最小堆。
但是这个题要有特别坑的一点:他存在这样一种情况:不用商业线到达不了终点的情况。所以要加以区分。
输出的时候也是有点问题:要从头到尾输出路径,这里可以存路径的前驱节点。然后从终点递归输出即可。

#include<bits/stdc++.h>
using namespace std;
#define maxn 505
#define inf 0x3f3f3f3f
#pragma warning(disable:4996)
int N,S,E;
int M,K;
int tot;int head[maxn];//用于前向星 
int dis[maxn][2];int vis[maxn]; int father[maxn][2];

struct Edge{
	int u,v,w,next;
}edge[maxn*maxn];

void add(int u,int v,int w)
{
	edge[tot].u=u;edge[tot].v=v;edge[tot].w=w;
	edge[tot].next=head[u];
	head[u]=tot;
	tot++;
}
priority_queue< pair<int,int> > q;
void dij(int s,int op){
	memset(vis,0,sizeof(vis));
	while(!q.empty()) q.pop();
	
	q.push(make_pair(0,s));
	dis[s][op]=0;
	father[s][op]=s;
	while(!q.empty())
	{
		int u=q.top().second;q.pop(); if(vis[u]==1)continue;vis[u]=1;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].v;int w=edge[i].w;
			if(dis[v][op]>w+dis[u][op])
			{
			    dis[v][op]=w+dis[u][op];
			    father[v][op]=u;
			    q.push(make_pair(-dis[v][op],v));
		    }
		}	
	}
}

void out(int x){
	if(father[x][0]==x)
	{
		printf("%d",x);return;
	}
	out(father[x][0]);
	printf(" %d",x);
}

int main(){
	int markk=0;
//	freopen("out.txt","w",stdout);
while(scanf("%d%d%d",&N,&S,&E)==3)
{ 
    scanf("%d",&M);int t1,t2,t3;
    memset(head,-1,sizeof(head));
    memset(dis,inf,sizeof(dis));
    memset(father,-1,sizeof(father));
    while(M--){scanf("%d%d%d",&t1,&t2,&t3);add(t1,t2,t3);add(t2,t1,t3);}
     dij(S,0);
	 dij(E,1);
	scanf("%d",&K);int a,b,c;
	 int hcu=-1;int hcv=-1;int min=dis[E][0];
	while(K--)
	{scanf("%d%d%d",&a,&b,&c);
	  if(dis[E][0]!=inf)
	  {  
	  	if(dis[a][0]+dis[b][1]+c<min)
      	{
      		hcu=a;hcv=b;
			min=dis[a][0]+dis[b][1]+c;
	  	}
	  	if(dis[b][0]+dis[a][1]+c<min)
	  	{ 
	  		hcu=b;hcv=a;
	  		min=dis[b][0]+dis[a][1]+c;
      	}
      } else
        {
        	//cout<<"bu可联通"<<endl; 
		     if(dis[a][0]!=inf&&dis[b][1]!=inf)
		     {
		     	hcu=a;hcv=b;min=dis[a][0]+dis[b][1]+c;
			 }
		     else if(dis[b][0]!=inf&&dis[a][1]!=inf)
		     {
		     	hcu=b;hcv=a;min=dis[b][0]+dis[a][1]+c;
			 }
	  }
	}
//	for(int i=1;i<=N;i++)
//	{ 
//	 for(int j=0;j<2;j++)
//	 {
//	 	if(dis[i][j]==inf)cout<<"inf"<<" ";else cout<<dis[i][j]<<" ";
//	 }cout<<endl;
//    }
	if(hcu==-1)
	{
	 if(markk==1)	 printf("\n");
	   out(E);printf("\n");
	   printf("Ticket Not Used\n");
	   	printf("%d\n",min);
    }
	else
	{
		//cout<<hcu<<father[1][0]<<father[2][0]<<father[3][0]<<father[4][0]<<endl;    
	  if(markk==1)printf("\n");
	 	out(hcu);
	 	for(int i=hcv;i!=E;i=father[i][1])printf(" %d",i);
	 	printf(" %d\n",E);
	 	printf("%d\n",hcu);
	 	printf("%d\n",min);
	}
   
markk=1;		
}
	
//	fclose(stdout);
	
	return 0;
} 

TT 的美梦:

在这里插入图片描述
这道题是使用了Bellman-Ford算法的队列优化即:SPFA算法。通常用于求含负权边的单源最短路径,以及判负权环。
SPFA算法和堆优化的Dijkstra算法很像,不同点在于SPFA算法可以重复入队,每次取出队首元素,该元素之后可能还会入队。Dijkstra则只能从堆中弹出一次。
这道题就是判断负权环的题目,存在负权环则最终权重一定为负无穷。由于图中的一条路径最长为n-1,所以如果路径大于等于n,就说明存在负权边。
当一个点入队n次后,则可以判断到该点存在负权边,于是该点能到达的所有点最短距离都为负无穷。所以可以从这个点进行DFS并标记。

#include<bits/stdc++.h>
using namespace std;//lightoj 1074
#define maxn 205
#define inf 0x3f3f3f3f
#pragma warning(disable:4996)
int N,M,Q;
int a[maxn];
int tot,head[maxn];//用于前向星 
int vis[maxn];int dis[maxn]; int cnt[maxn];int fh[maxn];

struct Edge{
	int u,v,w,next;
}edge[maxn*maxn*10];

void add(int u,int v,int w)
{
	edge[tot].u=u;edge[tot].v=v;edge[tot].w=w;
	edge[tot].next=head[u];
	head[u]=tot;
	tot++;
}
void dfs(int s){
	fh[s]=1;//cout<<s<<" "; 
	for(int i=head[s];i!=-1;i=edge[i].next)
	{
		if(fh[edge[i].v]==0)
		dfs(edge[i].v);
	}
	
}

void spfa(int s)
{
	memset(vis,0,sizeof(vis));
	memset(dis,inf,sizeof(dis));
	memset(cnt,0,sizeof(cnt));
	memset(fh,0,sizeof(fh));
	dis[s]=0;vis[s]=1;
	queue<int>q;while(!q.empty())q.pop();
    q.push(s);
    while(!q.empty())
    {    //cout<<"ffffffffffff"<<endl;
    	int u=q.front();q.pop();vis[u]=0;
    	for(int i=head[u];i!=-1;i=edge[i].next)
    	{  
    		int v=edge[i].v;int w=edge[i].w;
    		if(dis[v]>dis[u]+w)
    		{   
    			dis[v]=dis[u]+w;
    			cnt[v]=cnt[u]+1;
    			if(cnt[v]>=N){
    				dfs(v);
				}
    			else if(vis[v]==0) q.push(v),vis[v]=1;
			}
		}
	}
}



int main(){
int T;scanf("%d",&T);int _count=0;
while(T--)
{_count++;
	memset(head,-1,sizeof(head));
	scanf("%d",&N);
	for(int i=1;i<=N;i++)scanf("%d",&a[i]);
    
	scanf("%d",&M);
	for(int i=0;i<M;i++)
    {
       int ta,tb; 	scanf("%d%d",&ta,&tb);add(ta,tb,(a[tb]-a[ta])*(a[tb]-a[ta])*(a[tb]-a[ta])); 
	}
	
	scanf("%d",&Q);
	cout<<"Case "<<_count<<":"<<endl;
	for(int i=0;i<Q;i++)
	{
		spfa(1);//fflush(stdin);
		int e;cin>>e;
		if(dis[e]==inf||fh[e]==1||dis[e]<3)
		{
			printf("?\n");
		}
		else
		{
			printf("%d\n",dis[e]);
		}
	} 
		
}


	return 0;
} 

实验

CSP 2016-04-3: 路径解析
这道题的题意很难懂,讲的很绕脑子,总之就是让你看不懂。其实大概是这么个意思:给你一个相对/绝对路径 但是这个相对/绝对路径含有"…"、".“和多余的”/"。然后让你把他们化成标准的形式:也就是把"…"、".“和多余的”/“消去。并把相对化为绝对路径。
首先把相对路径化为绝对路径,我们只需要在相对路径前面加上输入中输入的当前目录,可以用string的+直接完成。
然后我们就用Vector存储字符串,用getline一次读取一行,在存储的时候把” / “消掉只存储路径字符串,输出的时候再加上。
然后判断是否是第一个” / “,消掉多余的” / “。然后消掉 " … " 和” . "即可。可以用vector的erase函数删除路径串。

#include<bits/stdc++.h>
using namespace std;

int n;
string now,t;
vector<string> vec[20];

void out(){
		for(int i=0;i<n;i++)
	    {
		if(vec[i].size()==0)
		{
			cout << "/" ;
		}
		else{
			for(int j=0;j<vec[i].size();j++)
			{
				cout << "/" ;
				if(j<vec[i].size())
				{
					cout << vec[i][j]; 
				} 
			}
		}
		
		cout << endl;
	}
}
int main(){
	cin>>n;cin>>now;
	cin.ignore();
	for(int i=0;i<n;i++)
	{t.clear();
	 getline(cin,t);
	 if(t[0]!='/')
	 {
	 	t=now+"/"+t;
	 }
	 string temp;
	 temp="";bool op=true;
	 for(int j=1;j<=t.size();j++)
	 {
	 	if(t[j]=='/'||j==t.size())
	 	{
	 		if(op==false)
	 		{
	 		 op=true;
			  vec[i].push_back(temp);
			  temp.clear();	
			 }
	        else if(op==true){
	         	continue;
			}
		 }
		 else{
		 	op=false;
		 	temp=temp+t[j];
		 }
	 	
	 }	
	}
	
	for(int i=0;i<n;i++)
	{
		int j=0;
		
	while(j<vec[i].size())
		{
			if(vec[i][j]=="..")
			{
				if(j==0)
				{
					vec[i].erase(vec[i].begin());
				}
				else{
				    j--;
					vec[i].erase(vec[i].begin()+j);
					vec[i].erase(vec[i].begin()+j);
				}
			}
			else if(vec[i][j]==".")
			{
				vec[i].erase(vec[i].begin()+j);
			}
		    else j++;
		}
	}

	out();
	
	
	
	
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值