【PAT】第十章 图算法专题(例题)

10.3 图的遍历

【例】A1013 Battle Over Cities (25 分)

ATTENTION

  • 要添加的边的数量为图中的连通块数-1。
  • 注意原图要进行多次查询,所以只能标记要删除的边,而不能对原图做修改!

并查集做法

#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;

const int maxn=1010;

int n,m,k;
int g[maxn][maxn]={0},fa[maxn];
bool vis[maxn];

int findFather(int a)
{
	if(fa[a]==a) return a;
	fa[a]=findFather(fa[a]); 	//路径压缩,否则超时 
	return  fa[a];
}
void Union(int a,int b)
{
	int faa=findFather(a);
	int fab=findFather(b);
	if(faa!=fab)
		fa[faa]=fab;
	return;
}

int main()
{
	scanf("%d %d %d",&n,&m,&k);
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		g[c1][c2]=1;
		g[c2][c1]=1;
	}
	for(int h=0;h<k;h++)
	{
		for(int i=1;i<=n;i++)
			fa[i]=i;
		fill(vis,vis+maxn,0);
		int tmp;
		scanf("%d",&tmp);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(i!=j&&i!=tmp&&j!=tmp&&g[i][j]!=0)
					Union(i,j);
		int cnt=0;
		for(int i=1;i<=n;i++)
			if(i!=tmp&&fa[i]==i) cnt++;
		printf("%d\n",cnt-1);
	}
}

遍历做法

#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;

const int maxn=1010;

int n,m,k;
int g[maxn][maxn]={0};
int idx[maxn]={0};
bool vis[maxn]={false};

void dfs(int u,int cnt,int tmp)
{
	vis[u]=true;
	idx[u]=cnt;
	for(int i=1;i<=n;i++)
		if(i!=tmp&&g[u][i]==1&&vis[i]==false)
			dfs(i,cnt,tmp);
}
int main()
{
	scanf("%d %d %d",&n,&m,&k);
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		g[c1][c2]=1;
		g[c2][c1]=1;
	}
	for(int i=0;i<k;i++)
	{
		int tmp=-1;
		fill(vis,vis+maxn,0);
		fill(idx,idx+maxn,0);
		scanf("%d",&tmp);

		int cnt=1;
		for(int j=1;j<=n;j++)
		{
			if(j!=tmp&&vis[j]==false)
			{
				dfs(j,cnt,tmp);
				cnt++;
			}	
		}
		set<int> theTmp;
		for(int i=1;i<=n;i++)
			if(i!=tmp) theTmp.insert(idx[i]);		
		printf("%d\n",theTmp.size()-1);
	}
	return 0;
}

【例】1021 Deepest Root (25 分)

acyclic a.无环的

ATTENTION

  • 这道题暴力进行ndfs其实也能过。
  • 最大树高/树的直径: 从树中找出任意一点,求出与他距离最远的点s,再用同样的方法求出与s距离最远的点ts-t即为树的直径。
  • 映射到本题即:如果只有一个连通分支,则先任选一个顶点进行一次dfs,记录下离起点最远的结点们。再从这些结点中选择一个进行dfs,得到离他最远的结点们,这两个结点们的集合就是所求。
  • 【注】在求树高的时候,不能在循环内部h++,否则对于当前顶点,其邻接点的高度就不同了。
#include <cstdio>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

const int maxn=10010;

int n,layer[maxn];
bool vis[maxn]={false};
vector<int> st1,adj[maxn];
int maxh=0;

void dfs(int s,int h)
{
	vis[s]=true;
	if(h>maxh)
	{
		st1.clear();
		st1.push_back(s);
		maxh=h;
	}
	else if(h==maxh)
		st1.push_back(s);
	for(int i=0;i<adj[s].size();i++)
	{
		if(vis[adj[s][i]]==false)
		{
			dfs(adj[s][i],h+1); 	//注意这里不能h++ 
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		adj[c1].push_back(c2);
		adj[c2].push_back(c1);
	}
	int cnt=0;
	for(int i=1;i<=n;i++)
	{
		if(vis[i]==false)
		{
			cnt++;
			dfs(i,1);
		}	
	}
	if(cnt>1)
		printf("Error: %d components",cnt);
	else
	{
		set<int> ans;int tmp;
		for(int i=0;i<st1.size();i++)
		{
			tmp=st1[0];
			ans.insert(st1[i]);
		}
		st1.clear();
		maxh=0;
		fill(vis,vis+maxn,0);
		dfs(tmp,1);
		for(int i=0;i<st1.size();i++)
			ans.insert(st1[i]);
		for(auto it=ans.begin();it!=ans.end();it++)
			printf("%d\n",*it);
	}
} 

暴力

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn=10010;

int n,layer[maxn];
bool vis[maxn]={false};
vector<int> ans,adj[maxn];

void dfs(int s,int &max)
{
	vis[s]=true;
	for(int i=0;i<adj[s].size();i++)
	{
		int v=adj[s][i];
		if(vis[v]==false)
		{
			layer[v]=layer[s]+1;
			if(layer[v]>max) max=layer[v];
			dfs(v,max);
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		adj[c1].push_back(c2);
		adj[c2].push_back(c1);
	}
	int max=-1,cnt=1;
	for(int i=1;i<=n;i++)
	{
		fill(vis,vis+n+1,0);
		fill(layer,layer+n+1,0);
		int tmp=0;
		dfs(i,tmp);
		bool flag=true;
		for(int j=1;j<=n;j++)
		{
			if(vis[j]==false)
			{
				int t;
				flag=false;
				cnt++;
				dfs(j,t);
			}
		}
		if(!flag) break;
		if(flag&&tmp>max)
		{
			ans.clear();
			max=tmp;
			ans.push_back(i);
		}
		else if(flag&&tmp==max)
			ans.push_back(i);
	}
	if(ans.size()==0)
		printf("Error: %d components",cnt);
	else
		for(int i=0;i<ans.size();i++)
			printf("%d\n",ans[i]);	
}

【例】*A1034 Head of a Gang (30 分)

ATTENTION

  • DFS模板并不会遍历图中所有的边,但这道题需要遍历一个连通分支例的所有边求边权和,所以要在DFS模板上进行修改。
  • 如果将DFS时所有结点的相邻边的边权全部相加,由于这是一个无向图,所以每条边都会被加两次,所以在每一个连通分支结束后,sum需要除以2。(晴神的做法是:在遍历到一条边g[u][v]后,将g[v][u]置0,即删除这条边,同时防止走回头路
  • 题目中给的n是边数,所以顶点数应该是<=2000。
  • 由于map<type1,type2>自动根据type1进行递增排序,所以将type1设为head的名字,就实现了字典序排序。
  • 点权可以在输入图时就计算得到!
  • 对于每个连通分支需要计算的信息,如head、sum等,在dfs的同时完成!
#include <string>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;

const int maxn=2010;
const int inf=1e9+10;

int n,k,idx=0;
int g[maxn][maxn],weight[maxn]={0};
bool vis[maxn]={false};
map<string,int> nameToIdx;
map<int,string> idxToName;
map<string,int> headToCnt;

void dfs(int u,int &head,int &cnt,int &sum)
{
	vis[u]=true;
	
	for(int i=1;i<=idx;i++)
	{
		if(g[u][i]!=inf)
		{
			if(weight[u]>weight[head]) 
				head=u;
			sum+=g[u][i];
			if(vis[i]==false) 
			{
				cnt++;
				dfs(i,head,cnt,sum);
			}
		}
	}	
}
void dfsTrave()
{
	for(int i=1;i<=idx;i++)
	{
		int max=0,head=-1,cnt=0,sum=0;
		if(vis[i]==false)
		{
			max=weight[i],head=i,cnt=1,sum=0;
			dfs(i,head,cnt,sum);
			//cout<<head<<" "<<cnt<<" "<<sum<<"\n"; 
		}
		if(cnt>2&&sum>2*k)
		{
			headToCnt[idxToName[head]]=cnt;
		}
	}
}
int main()
{
	cin>>n>>k;
	fill(g[0],g[0]+maxn*maxn,inf);
	for(int i=0;i<n;i++)
	{
		string name1,name2;
		int time;
		cin>>name1>>name2>>time;
		if(nameToIdx[name1]==0)
		{
			idx++;
			nameToIdx[name1]=idx;
			idxToName[idx]=name1;
		}
		if(nameToIdx[name2]==0)
		{
			idx++;
			nameToIdx[name2]=idx;
			idxToName[idx]=name2;
		}
		int u=nameToIdx[name1];
		int v=nameToIdx[name2];
		if(g[u][v]==inf||g[v][u]==inf)
		{
			g[u][v]=time;
			g[v][u]=time;
		}
		else
		{
			g[u][v]+=time;
			g[v][u]+=time;
		}
		weight[u]+=time;
		weight[v]+=time; 	//点权可以在创建图的时候计算得到 
	}
	dfsTrave();
	cout<<headToCnt.size()<<"\n";
	for(map<string,int>::iterator it=headToCnt.begin();it!=headToCnt.end();it++)
		cout<<it->first<<" "<<it->second<<"\n";
	return 0;
}

【例】A1154 Vertex Coloring (25 分)

labeling n. 标签;标记;[计] 标号
vertex n. 顶点
indices n. 指数;目录(index的复数)

ATTENTION

  • 因为有多组数据,要注意清空。PAT很少考一个test里多组数据。其实在每个test里声明vector存储顶点颜色。
  • 用邻接矩阵会超内存,顶点数<=1000采用邻接矩阵!!!
  • 遍历所有顶点的邻接点,看颜色有没有重复,别忘了把该顶点的颜色也算进去(五分啊五分)。
  • 细节细节细节!!!你很容易在细节上扣分,下笔谨慎些。
#include <cstdio>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;

const int maxn=10010;
const int INF=1e9+10;

int n,m,k;
vector<int> g[maxn];
int color[maxn];
set<int> colorNum;

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int u,v;
		scanf("%d %d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	scanf("%d",&k);
	
	for(int i=0;i<k;i++)
	{
		fill(color,color+maxn,0);
		colorNum.clear();
		
		for(int j=0;j<n;j++)
			scanf("%d",&color[j]);
			
		bool ans=true;
		
		for(int u=0;u<n;u++)
		{
			bool flag=true;
			colorNum.insert(color[u]); 	//忘记把第一个结点的颜色插入set了 orz 
			for(vector<int>::iterator it=g[u].begin();it!=g[u].end();it++)
			{
				if(color[u]==color[*it])
				{
					flag=false;
					break;
				}
				else
					colorNum.insert(color[*it]); 
			}
			if(flag==false)
			{
				ans=false;
				break;
			}
		}
		if(ans)
			printf("%d-coloring\n",colorNum.size());
		else
			printf("No\n");
	}
	
	return 0;
}

【例】A1134 Vertex Cover (25 分)

incident adj. [光] 入射的;附带的,附属的;伴随而来的

ATTENTION

  • 第一思路是遍历整个图所有的边。所以把这道题放在图的遍历这里了。
  • 柳神太强了。我怎么就没想到考虑点和边的对应关系呢orz 转换成散列哈希太强了TAT。换位思考,主客互换。

渣渣做法:

#include <cstdio>
#include <vector>
#include <map>
using namespace std;

int n,m,k,nv;
vector<int> g[10010];
map<int,int> mp;

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		g[c1].push_back(c2);
		g[c2].push_back(c1);
	}
	scanf("%d",&k);
	for(int i=0;i<k;i++)
	{
		mp.clear();
		scanf("%d",&nv);
		for(int j=0;j<nv;j++)
		{
			int tmp;
			scanf("%d",&tmp);
			mp[tmp]=1;
		}
		//judge
		bool isYes=true;;
		for(int j=0;j<n;j++)
		{
			if(mp[j]==1) continue;
			bool flag=true;
			for(int v=0;v<g[j].size();v++)
			{
				if(mp[g[j][v]]==0)
				{
					flag=false;
					break;
				}
			}
			if(!flag)
			{
				isYes=false;
				printf("No\n");break;
			}
		}
		if(isYes) printf("Yes\n");
	}
	
}

大神版:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int n,m,k,nv;
bool vis[10010]={0};
vector<int> v[10010];

int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		cin>>c1>>c2;
		v[c1].push_back(i);
		v[c2].push_back(i);
	}
	cin>>k;
	while(k--)
	{
		fill(vis,vis+m,0);
		cin>>nv;
		int flag=0;
		for(int i=0;i<nv;i++)
		{
			int u;
			cin>>u;
			for(int j=0;j<v[u].size();j++)
				vis[v[u][j]]=true;
		}
		for(int i=0;i<m;i++)
		{
			if(vis[i]==false)
			{
				flag=-1;
				break;
			}
		}
		if(flag==0) cout<<"Yes\n";
		else cout<<"No\n";
	}
}

【例】A1076 Forwards on Weibo (30 分)

foward v.转发
follower n.跟随者;粉丝

ATTENTION

  • 这道题的边是有向边,且input的方向与实际遍历的边的方向相反。
  • 因为可能产生环,所以要确保每个结点只会被bfs到一次。
  • 起始节点layer=0
  • 如果用结构体
 struct node{
 	int id; 	//结点编号
 	int layer; 	//层数
 };
#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

const int maxn=1010;

int n,l,k,layer[maxn]={0};
vector<int> adj[maxn];
bool inq[maxn]={false};


int bfs(int u)
{
	queue<int> q;
	q.push(u);
	inq[u]=true;
	int cnt=0;
	layer[u]=0;
	while(!q.empty())
	{
		int front=q.front();
		if(layer[front]>=l)
			return cnt;
		q.pop();
		for(int i=0;i<adj[front].size();i++)
		{
			int v=adj[front][i];
			if(inq[v]==false)
			{
				layer[v]=layer[front]+1;
				cnt++;
				q.push(v);
				inq[v]=true;
			}
		}
	}
}
int main()
{
	scanf("%d %d",&n,&l);
	for(int i=1;i<=n;i++)
	{
		int nv,v;
		scanf("%d",&nv);
		for(int j=0;j<nv;j++)
		{
			scanf("%d",&v);
			adj[v].push_back(i);
		}	
	}
	scanf("%d",&k);
	for(int i=0;i<k;i++)
	{
		fill(inq,inq+maxn,0);
		fill(layer,layer+maxn,0);
		int u;
		scanf("%d",&u);
		int ans=bfs(u);
		printf("%d\n",ans);
	}
}


【例】A1126 Eulerian Path (25 分)

circuit n.环,回路

ATTENTION

  • 注意欧拉图和半欧拉图都要是连通的。
#include <cstdio>

int n,m;
int de[510]={0};
int g[510][510]={0};
bool vis[510]={false};

void dfs(int u)
{
	vis[u]=true;
	for(int v=1;v<=n;v++)
	{
		if(vis[v]==false&&g[u][v]==1)
			dfs(v);
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		de[c1]++;
		de[c2]++;
		g[c1][c2]=g[c2][c1]=1;
	}
	int even=0,odd=0;
	for(int i=1;i<=n;i++)
	{
		if(de[i]%2==0)	even++;
		else odd++;
		if(i==1) printf("%d",de[i]);
		else printf(" %d",de[i]);
	}
	bool flag=true;
	dfs(1);
	for(int i=1;i<=n;i++)
	{
		if(vis[i]==false)
		{
			flag=false;
			break;
		}
	}
	if(flag&&even==n) printf("\nEulerian");
	else if(flag&&even==n-2&&odd==2) printf("\nSemi-Eulerian");
	else printf("\nNon-Eulerian");
	return 0;
}

10.4 最短路径

10.4.1 迪杰斯特拉算法

【例】A1003 Emergency
#include <cstdio>
#include <algorithm>
using namespace std; 	//使用algorithm需要这句话

const int MAXN=1000;
const int INF=1e9;
int n,m,s,t;
int G[MAXN][MAXN];
bool vis[MAXN]={false};
int d[MAXN]; 	//最短路 
int rescueNum[MAXN];
int maxRes[MAXN]; 	//最大救援人数 
int maxNum[MAXN];

void dij(int s)
{
	//initial
	fill(d,d+MAXN,INF);
	d[s]=0;
	fill(maxRes,maxRes+MAXN,0);
	maxRes[s]=rescueNum[s];
	fill(maxNum,maxNum+MAXN,0);
	maxNum[s]=1; 	//最短路条数
	//n次循环 
	for(int i=0;i<n;i++)
	{
		//寻找最近的那个
		int u=-1,min=INF;
		for(int j=0;j<n;j++)
		{
			if(vis[j]==false&&d[j]<min)
			{
				u=j;
				min=d[u];
			}
		}
		if(u==-1)	return; 	//!!!!!!!!!!
		//update
		vis[u]=true;
		for(int v=0;v<n;v++)
		{
			if(vis[v]==false&&G[u][v]!=INF)
			{
				if(d[v]>d[u]+G[u][v])
				{
					d[v]=d[u]+G[u][v];
					maxRes[v]=maxRes[u]+rescueNum[v];
					maxNum[v]=maxNum[u]; 	//这里很重要!!!!!!!!!
				}
				else if(d[v]==d[u]+G[u][v])
				{
					maxNum[v]+=maxNum[u]; 	//这里很重要!!!!!!!!!
					if(maxRes[v]<maxRes[u]+rescueNum[v])
						maxRes[v]=maxRes[u]+rescueNum[v];
				}
			}
		} 
	}
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);
	
	fill(G[0],G[0]+MAXN*MAXN,INF); 	//图的初始化,很重要!!! 
	for(int i=0;i<n;i++)
	{
		scanf("%d",&rescueNum[i]);
	} 
	for(int i=0;i<m;i++)
	{
		int c1,c2,l;
		scanf("%d%d%d",&c1,&c2,&l);
		G[c1][c2]=l;
		G[c2][c1]=l;
	}
	dij(s);
	printf("%d %d",maxNum[t],maxRes[t]);
	
}

【例】A1018 Public Bike Management (30 分)

ATTENTION

  • 一开始竟然忘记 初始化图 了,你这个大憨憨!!!
  • 注意选择的路径上所有的station都要变得perfect,所以要确保每个的weight都为0。还要注意tmpPath是倒序存储的,所以要倒着调整。
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn=510;
const int inf=1e9+10;

int cmax,n,sp,m,g[maxn][maxn],weight[maxn];
int d[maxn];
bool vis[maxn];
vector<int> pre[maxn];


void dij(int s)
{
	fill(d,d+maxn,inf);
	fill(vis,vis+maxn,0);
	d[s]=0;
	
	for(int i=0;i<=n;i++)
	{
		int u=-1,min=inf;
		for(int j=0;j<=n;j++)
		{
			if(vis[j]==false&&d[j]<min)
			{
				u=j;
				min=d[j];
			}
		}
		if(u==-1) return;
		
		vis[u]=true;
		for(int i=0;i<=n;i++)
		{
			if(vis[i]==false&&g[u][i]!=inf)
			{
				if(d[i]>d[u]+g[u][i])
				{
					pre[i].clear();
					pre[i].push_back(u);
					d[i]=d[u]+g[u][i];
				}
				else if(d[i]==d[u]+g[u][i])
				{
					pre[i].push_back(u);
				}
			}
		}
	}
}
vector<int> tmpPath,path;
int sendcnt=inf,backcnt=inf;
void findPath(int u)
{
	if(u==0)
	{
		tmpPath.push_back(u);
//		for(int i=0;i<tmpPath.size();i++)
//			printf("%d",tmpPath[i]);
//		printf("\n");
		int tmps=0,tmpc=0;
		for(int i=tmpPath.size()-2;i>=1;i--)
		{
			int v=tmpPath[i];
			if(weight[v]>=0) tmpc+=weight[v];
			else
			{
				if(tmpc+weight[v]<0)
				{
					tmps=0-tmpc-weight[v];
					tmpc=0;
				}
				else
				{
					tmpc+=weight[v];
				}
			}
		}
		if(weight[sp]>=0) tmpc+=weight[sp];
		else
		{
			if(tmpc+weight[sp]<0)
			{
				tmps+=0-tmpc-weight[sp];
				tmpc=0;
			} 
			else tmpc+=weight[sp];
		}
		if(tmps<sendcnt)
		{
			sendcnt=tmps;
			path=tmpPath;
			backcnt=tmpc;
		}
		else if(tmps==sendcnt)
		{
			if(backcnt>tmpc)
			{
				path=tmpPath;
				backcnt=tmpc;
			}
		}
		tmpPath.pop_back();
		return ;
	}
	tmpPath.push_back(u);
	for(int i=0;i<pre[u].size();i++)
	{
		findPath(pre[u][i]);
	}
	tmpPath.pop_back();
}
int main()
{
	fill(g[0],g[0]+maxn*maxn,inf);
	scanf("%d %d %d %d",&cmax,&n,&sp,&m);
	for(int i=1;i<=n;i++) scanf("%d",&weight[i]);
	for(int i=1;i<=n;i++)
		weight[i]-=cmax/2;
	weight[0]=0;
	for(int i=0;i<m;i++)
	{
		int s1,s2,t;
		scanf("%d %d %d",&s1,&s2,&t);
		g[s1][s2]=g[s2][s1]=t;
	}
	dij(0);
	findPath(sp);
	printf("%d ",sendcnt);
	for(int i=path.size()-1;i>=0;i--)
	{
		if(i==path.size()-1) printf("%d",path[i]);
		else printf("->%d",path[i]);
	}
	printf(" %d",backcnt);
	return 0;
}

【例】*A1030 Travel Plan

【例】*A1087 All Roads Lead to Rome (30 分)

client n.客户;顾客;委托人

ATTENTION

  • Dij+DFS太不熟练了啊啊啊啊啊!!!理解还不够!!!!!!

  • 这道题有三个标尺。
    第一标尺:cost最小
    第二标尺:happiness最大
    第三标尺:average happiness 最大
    同时,还要输出最短路径的数量。

  • 前两个标尺和最短路径的数量可以根据笔记直接求得。对于max average happiness,需要额外记录从起点到顶点u之间的顶点数目(记录在数组pt[]中)。
    那么就是,如果以点u为中介点,可以优化路径,那么pt[v]=pt[u]+1

  • 因为顶点是三个字母表示的,一开始的想法是用字符串hash表示每个顶点,那么maxn=262626+10。但是没想到超内存了!!!事实上,可以把顶点的输入保存下来,用map结构与0~N-1映射!!!

  • 思想不要那么僵化呀啊啊啊啊啊啊!!!

  • 还有,自己在写的时候,输出路径极为不熟练!!!!!!

  • 这道题真的写得心态爆炸哦。至今有两次写dij不了了之,这可能也是为什么我对dij有种畏惧。其中有一次应该是因为图没有初始化为不可达。这次也是,debug了好久发现有个for循环的范围搞错了。这也反映了自己对算法的不理解。还有待练习!

  • 注意!!!输出路径的时候,要return啊啊啊啊啊!!!

  • 还有!fill函数和memset函数!!!

 #include <string.h>
 memset(数组名,,sizeof(数组名)); 	//按字节赋值,只赋值0或-1
#include <algorithm>
using namespace std;
fill(数组名,数组名+大小,); 	//可以使任意值
//二维数组初始化
fill(g[0],g[0]+maxn*maxn,INF);
#include <iostream>
#include <string.h>
#include <string>
#include <map>
#include <algorithm> 	//fill
using namespace std;

const int maxn=210; 	//最大顶点数 
const int INF=1e9; 	//不可达

int n,k,st; 	//st 起点 
int g[maxn][maxn],weight[maxn]; 	//图,点权 
//最短路径,最大点权,最短路径数目,目前路径上的顶点数,前驱结点 
int d[maxn],w[maxn],num[maxn],pt[maxn],pre[maxn];
bool vis[maxn]={0}; 
map<string,int> cityToIdx; 	//城市名转换为idx 
map<int,string> idxToCity; 	//idx转换为城市名

void dij(int s) 
{
	fill(d,d+maxn,INF);
	memset(w,0,sizeof(w));
	memset(num,0,sizeof(num));
	memset(pt,0,sizeof(pt));
	for(int i=0;i<n;i++) 
		pre[i]=i;
	d[s]=0;
	num[s]=1;
	w[s]=weight[s];
	for(int i=0;i<n;i++){
		int u=-1,min=INF;
		for(int j=0;j<n;j++){
			if(vis[j]==false&&d[j]<min){
				u=j;
				min=d[j];
			}
	}
			
			if(u==-1) return;
			
			vis[u]=true;
			
			for(int v=0;v<n;v++){
				if(vis[v]==false&&g[u][v]!=INF){
					if(d[v]>d[u]+g[u][v]){
						d[v]=g[u][v]+d[u];
						pre[v]=u;
						pt[v]=pt[u]+1;
						num[v]=num[u];
						w[v]=w[u]+weight[v];
					}
					else if(d[v]==g[u][v]+d[u]){
						num[v]+=num[u];
						if(w[v]<w[u]+weight[v]){
							pre[v]=u;
							pt[v]=pt[u]+1;
							w[v]=w[u]+weight[v];
						}
						else if(w[v]==w[u]+weight[v]){
							double uAvg=(w[u]+weight[v])*1.0/(pt[u]+1);
							double vAvg=(w[v])*1.0/(pt[v]);
							if(uAvg>vAvg){
								pt[v]=pt[u]+1;
								pre[v]=u; 	//!!!
							}
						}
					}
				}	
			}
		}
}
void printPath(int v)
{
	if(v==0)
	{
		cout<<idxToCity[v];
		return;
	}
	printPath(pre[v]);
	cout<<"->"<<idxToCity[v];
}
int main()
{
	string str;
	cin>>n>>k>>str;
	cityToIdx[str]=0;
	idxToCity[0]=str;
	for(int i=1;i<n;i++)
	{
		cin>>str;
		cin>>weight[i];
		cityToIdx[str]=i;
		idxToCity[i]=str;
	}
	fill(g[0],g[0]+maxn*maxn,INF); 	//!!!!!!!!! 
	for(int i=0;i<k;i++)
	{
		string c1,c2;
		int i1,i2;
		cin>>c1>>c2;
		i1=cityToIdx[c1];i2=cityToIdx[c2];
		cin>>g[i1][i2];
		g[i2][i1]=g[i1][i2];
		//cout<<i1<<" "<<i2<<" "<<g[i1][i2]<<endl; 
	}
	dij(0);
	int end=cityToIdx["ROM"];
	cout<<num[end]<<" "<<d[end]<<" "<<w[end]<<" "<<w[end]/pt[end]<<endl;
	printPath(end);
	return 0;
	
}

Dij+DFS 做法
ATTENTION

  • 别忘了维护string->intint->string的两组映射!
  • 这里只记录了least cost和max happiness的最短路径,但是由于题目要求的最短路径的数量是least cost的,所以在dij中计算最短路径的数量!
  • tempPathpop_back()还不是很懂,还要再理解一下!
  • 注意average happiness只要输出整数部分。
#include <iostream>
#include <string>
#include <string.h> 	//memset头文件 
#include <vector>
#include <algorithm> 	//fill头文件 
#include <map>
using namespace std;

const int maxn=210;
const int INF=1e9;

int n,k;
string st;
int g[maxn][maxn],d[maxn]; 	//图,cost 
bool vis[maxn]={0};
int weight[maxn],w[maxn]; 	//点权 
int num[maxn]={0}; 
map<string,int> cityToIdx;
map<int,string> idxToCity;

vector<int> pre[maxn]; 	//前驱结点
vector<int> tempPath,path;
double happyMax=0;

void dij(int s)
{
	fill(d,d+maxn,INF);
	memset(w,0,sizeof(w)); 	//memset用法 
	d[s]=0;
	w[s]=0;
	num[s]=1;
	
	for(int i=0;i<n;i++)
	{
		int u=-1,min=INF;
		for(int j=0;j<n;j++)
		{
			if(vis[j]==false&&d[j]<min)
			{
				u=j;
				min=d[j]; 
			}
		}
		
		if(u==-1) return;
		
		vis[u]=true;
		
		for(int v=0;v<n;v++)
		{
			if(vis[v]==false&&g[u][v]!=INF)
			{
				if(d[v]>d[u]+g[u][v])
				{
					d[v]=d[u]+g[u][v];
					w[v]=w[u]+weight[v];
					pre[v].clear();
					pre[v].push_back(u);
					num[v]=num[u];
				}
				else if(d[v]==d[u]+g[u][v])
				{
					pre[v].push_back(u);
					num[v]+=num[u]; 	//!!!
					if(w[v]<w[u]+weight[v])
					{
						w[v]=w[u]+weight[v];
						pre[v].clear();
						pre[v].push_back(u);	
					}
					else if(w[v]==w[u]+weight[v])
					{
						pre[v].push_back(u);
					}
				}
			}
		}
	} 
}
void dfs(int t)
{
	if(t==0) 	//到达起点,一条最短路径 
	{ 
		tempPath.push_back(t);
		int cnt=tempPath.size()-1;
		double cur=w[tempPath[0]]*1.0/cnt;
		if(cur>happyMax)
		{
			path=tempPath;
			happyMax=cur;
		}
		tempPath.pop_back(); 	//!!!!!!!!!
	}
	tempPath.push_back(t);
	for(int i=0;i<pre[t].size();i++)
	{
		dfs(pre[t][i]);
	}
	tempPath.pop_back();
}
int main()
{
	cin>>n>>k>>st;
	cityToIdx[st]=0;
	idxToCity[0]=st;
	for(int i=1;i<n;i++)
	{
		string str;int tmp;
		cin>>str>>tmp;
		cityToIdx[str]=i;
		idxToCity[i]=str; 	//很重要!!! 
		weight[i]=tmp;
	}
	fill(g[0],g[0]+maxn*maxn,INF); 
	for(int i=0;i<k;i++)
	{
		string c1,c2;
		cin>>c1>>c2;
		int a=cityToIdx[c1],b=cityToIdx[c2];
		cin>>g[a][b];
		g[b][a]=g[a][b];
	}
	dij(0);
	int end=cityToIdx["ROM"];
	dfs(end);
	cout<<num[end]<<" "<<d[end]<<" "<<w[end]<<" "<<(int)happyMax<<endl;
	for(int i=path.size()-1;i>=0;i--)
	{
		string s=idxToCity[path[i]];
		if(i!=0)
			cout<<s<<"->";
		else
			cout<<s<<endl;
	}
	return 0;
}

【例】*A1072 Gas Station (30 分)

ATTENTION

  • 这题好坑啊,样例一3.25精确到1位成了3.3,害得我以为要进位,然后最后一个点就死活过不去了呜呜呜TAT
  • 看到accurate up to x decimal places,无脑用%.md即可!!!
#include <string>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn=1020;
const int INF=1e9;

int n,m,k,ds;
int g[maxn][maxn],d[maxn];
bool vis[maxn]={0};

int maxDis=0,idx=0;
double avgDis=0;

bool dij(int s)
{
	fill(d,d+maxn,INF);
	fill(vis,vis+maxn,0);
	d[s]=0;
	
	for(int i=1;i<=n+10;i++)
	{
		int u=0,min=INF;
		for(int j=1;j<=10+n;j++)
		{
			if(vis[j]==false&&d[j]<min)
			{
				u=j;
				min=d[j];
			}
		}
		if(u==0) return false;
		vis[u]=true;
		for(int v=1;v<=n+10;v++)
		{
			if(vis[v]==false&&g[u][v]!=INF)
			{
				if(d[v]>d[u]+g[u][v])
					d[v]=d[u]+g[u][v];
			}
		}
	}
	return true;
}
int strToIdx(string s)
{
	int ans=0;
	for(int i=0;i<s.size();i++)
	{
		ans=ans*10+s[i]-'0';
	}
	return ans+10;
}
int main()
{
	cin>>n>>m>>k>>ds;
	fill(g[0],g[0]+maxn*maxn,INF);
	for(int i=1;i<=m+n;i++)
		g[i][i]=0;
	for(int i=0;i<k;i++)
	{
		string s1,s2;int h1,h2;
		cin>>s1>>s2;
		if(s1[0]!='G') h1=strToIdx(s1); 	//房子 11-n+10 
		else 
		{
			if(s1.size()==3)
				h1=10;
			else
				h1=s1[1]-'0'; 	//加油站 1-10 
		}
		
		if(s2[0]!='G') h2=strToIdx(s2); 	//房子 11-n+10 
		else 
		{
			if(s2.size()==3)
				h2=10;
			else
				h2=s2[1]-'0'; 	//加油站 1-10 
		}
		int w;
		cin>>w;
		if(w<g[h1][h2])
		{
			g[h1][h2]=w;
			g[h2][h1]=w;
		}
	}
	bool flag;
	for(int i=1;i<=m;i++) 	//对于每一个候选车站,进行dfs
	{
		flag=true; 	//!!!
		dij(i);
		int tmp=INF;
		double sum=0,avgSum=0;
		for(int j=11;j<=10+n;j++)
		{
			sum+=d[j];
			if(d[j]>ds) 	//不在服务范围内 
			{
				flag=false;
				break;
			}
			if(d[j]<tmp) 	//最小dis 
				tmp=d[j];	
		}
		if(flag)
		{
			avgSum=sum*1.0/(double)n;
			if(tmp>maxDis)
			{
				maxDis=tmp;
				avgDis=avgSum;
				idx=i;
			}
			else if(tmp==maxDis)
			{
				if(avgSum<avgDis)
				{
					avgDis=avgSum;
					idx=i;
				}
			}	
		}
	}
	if(idx!=0)
	{
		cout<<"G"<<idx<<"\n";
		printf("%.1f %.1f\n",(double)maxDis,avgDis);
	}
	else
	{
		cout<<"No Solution"<<endl;
	}
	return 0;	 
}

【例】A1111 Online Map (30 分)

intersection n. 交叉;十字路口;交集;交叉点
identical adj. 同一的;完全相同的

ATTENTION

  • 这里有个有向边的问题。one-way如果为1,则这条路是单向的。
//intersection identical 
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn=510;
const int inf=1e9+10;

int len[maxn][maxn],time[maxn][maxn];
bool vis[maxn];
int d[maxn],t[maxn],n,m,pred[maxn],s,de;
vector<int> pre[maxn];
vector<int> shortest,fastest;

void dfsDis(int s)
{
	fill(vis,vis+maxn,0); 	/
	fill(d,d+maxn,inf);
	fill(t,t+maxn,inf);
	for(int i=0;i<n;i++) pred[i]=i;
	d[s]=0;
	t[s]=0;
	
	for(int i=0;i<n;i++)
	{
		int u=-1,min=inf;
		for(int j=0;j<n;j++)
		{
			if(vis[j]==false&&min>d[j])
			{
				u=j;
				min=d[j];
			}
		}
		if(u==-1) return;
		vis[u]=true; 	/	
		for(int v=0;v<n;v++)
		{
			if(vis[v]==false)
			{
				if(d[v]>d[u]+len[u][v])
				{
					d[v]=d[u]+len[u][v];
					pred[v]=u;
					t[v]=t[u]+time[u][v];
				}
				else if(d[v]==d[u]+len[u][v]&&t[v]>t[u]+time[u][v])
				{
					pred[v]=u;
					t[v]=t[u]+time[u][v];
				}
			}
		}
	}
}
void getPath(int tt)
{
	if(tt==s)
	{
		shortest.push_back(tt);
		return;
	}
	shortest.push_back(tt);
	getPath(pred[tt]);
}
void dfsTime(int s)
{
	fill(vis,vis+maxn,0); 	/
	fill(t,t+maxn,inf);
	t[s]=0;
	
	for(int i=0;i<n;i++)
	{
		int u=-1,min=inf;
		for(int j=0;j<n;j++)
		{
			if(vis[j]==false&&min>t[j])
			{
				u=j;
				min=t[j];
			}
		}
		if(u==-1) return;
		vis[u]=true; 	/	
		for(int v=0;v<n;v++)
		{
			if(vis[v]==false)
			{
				if(t[v]>t[u]+time[u][v])
				{
					pre[v].clear();
					pre[v].push_back(u);
					t[v]=t[u]+time[u][v];
				}
				else if(t[v]==t[u]+time[u][v])
				{
					pre[v].push_back(u);
				}
			}
		}
	}
}
vector<int> tmp;
int minIn=inf;
void getFast(int tt)
{
	if(tt==s)
	{
		tmp.push_back(tt);
		if(tmp.size()<minIn)
		{
			minIn=tmp.size();
			fastest=tmp;
		}
		tmp.pop_back();
		return ;
	}
	tmp.push_back(tt);
	for(int i=0;i<pre[tt].size();i++)
		getFast(pre[tt][i]);
	tmp.pop_back();
}
int main()
{
	scanf("%d %d",&n,&m);
	fill(len[0],len[0]+maxn*maxn,inf); 	/
	fill(time[0],time[0]+maxn*maxn,inf);
	for(int i=0;i<m;i++)
	{
		int v1,v2,one_way,length,cost;
		scanf("%d %d %d %d %d",&v1,&v2,&one_way,&length,&cost);
		len[v1][v2]=length;
		time[v1][v2]=cost;
		if(one_way==0)
		{
			len[v2][v1]=length;
			time[v2][v1]=cost;
		}
	}
	scanf("%d %d",&s,&de);
	dfsDis(s);
	getPath(de);
//	for(int i=0;i<shortest.size();i++)
//		printf("%d ",shortest[i]);
	dfsTime(s);
	getFast(de);
//	for(int i=0;i<fastest.size();i++)
//		printf("%d ",fastest[i]);
	if(fastest==shortest)
	{
		printf("Distance = %d; Time = %d:",d[de],t[de]);
		for(int i=fastest.size()-1;i>=0;i--)
		{
			printf(" %d",fastest[i]);
			if(i!=0) printf(" ->");
		}
	}
	else
	{
		printf("Distance = %d:",d[de]);
		for(int i=shortest.size()-1;i>=0;i--)
		{
			printf(" %d",shortest[i]);
			if(i!=0) printf(" ->");
		}
		printf("\nTime = %d:",t[de]);
		for(int i=fastest.size()-1;i>=0;i--)
		{
			printf(" %d",fastest[i]);
			if(i!=0) printf(" ->");
		}
	}
	return 0;
}

10. 拓扑排序

【例】A1146 Topological Order (25 分)

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn=1010;

int n,m,k;
vector<int> adj[maxn];
vector<int> ans;
bool vis[maxn]={false};

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		scanf("%d %d",&c2,&c1);
		adj[c1].push_back(c2);
	}
	scanf("%d",&k);
	for(int i=0;i<k;i++)
	{
		fill(vis,vis+n,0);
		int u;
		int cnt=0;
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&u);
			for(int h=0;h<adj[u].size();h++)
			{
				if(vis[adj[u][h]]==false)
					cnt++;
			}
			vis[u]=true;	
		}
		if(cnt!=0)
			ans.push_back(i);
	}
	for(int i=0;i<ans.size();i++)
	{
		if(i==0) printf("%d",ans[i]);
		else printf(" %d",ans[i]);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法sicily例题 1000. sicily 1155. Can I Post the lette Time Limit: 1sec Memory Limit:32MB Description I am a traveler. I want to post a letter to Merlin. But because there are so many roads I can walk through, and maybe I can’t go to Merlin’s house following these roads, I must judge whether I can post the letter to Merlin before starting my travel. Suppose the cities are numbered from 0 to N-1, I am at city 0, and Merlin is at city N-1. And there are M roads I can walk through, each of which connects two cities. Please note that each road is direct, i.e. a road from A to B does not indicate a road from B to A. Please help me to find out whether I could go to Merlin’s house or not. Input There are multiple input cases. For one case, first are two lines of two integers N and M, (N<=200, M<=N*N/2), that means the number of citys and the number of roads. And Merlin stands at city N-1. After that, there are M lines. Each line contains two integers i and j, what means that there is a road from city i to city j. The input is terminated by N=0. Output For each test case, if I can post the letter print “I can post the letter” in one line, otherwise print “I can't post the letter”. Sample Input 3 2 0 1 1 2 3 1 0 1 0 Sample Output I can post the letter I can't post the letter Source Code #include <iostream> #include <vector> using namespace std; int n,m; vector<int> vout[200]; bool visited[200]; bool flood(int u) { visited[u]=1; if (u==n-1) return 1; for (int x=0; x<vout[u].size(); x++) { int &v=vout[u][x]; if (!visited[v] && flood(v)) return 1; } return 0; }
### 回答1: Pat晴神宝典是一本关于算法的笔记。它由知名的算法专家Pat编写,是一本系统全面的算法学习资料。这本宝典包含了许多不同类型的算法,涉及了各个领域的问题解决方法。 Pat晴神宝典首先介绍了算法的基础知识,如时间复杂度、空间复杂度、数据结构等。然后它深入探讨了各种经典的算法,如排序算法、搜索算法算法等。每个算法都有详细的说明和代码实现,以及算法的优缺点和应用场景。宝典还提供了大量的练习题和习题解答,帮助读者巩固所学的算法知识。 Pat晴神宝典不仅仅是一个算法的技术手册,它还讲述了算法的设计思想和解题方法。宝典中有许多实际问题的案例分析,解释了如何使用不同的算法解决实际问题。它还提供了一些常见算法的优化方法和改进思路,帮助读者更好地掌握算法的运用。 总结来说,Pat晴神宝典是一本涵盖了广泛的算法知识的笔记。无论是初学者还是有经验的程序员,都可以从中获得宝贵的算法学习和应用经验。它不仅帮助读者提高解决问题的能力,还培养了读者的算法思维和创造力。无论在学术研究还是工程开发中,这本宝典都是一本不可或缺的参考书。 ### 回答2: PAT晴神宝典是一本算法学习笔记。PAT(Programming Ability Test)是中国程序设计竞赛的一种形式,它旨在提高学生的程序设计能力和算法思维能力。晴神指的是晴天老师,他是一位在算法竞赛界非常有影响力的老师,他编写了《算法笔记》一书。 这本《算法笔记》包含了程序设计竞赛中常用的数据结构和算法的讲解和实践。它主要分为四个部分:基础部分、数据结构、算法以及习题。基础部分主要介绍了程序设计的基本开发环境以及常用的算法思想和技巧,如递归、分治和动态规划等。数据结构部分涵盖了常见的数据结构,如树、和堆等,以及它们的实现和应用。算法部分介绍了各种算法的设计思想和实现方法,如贪心算法、搜索算法算法等。习题部分提供了大量的练习题,并给出了详细的解题思路和代码。 《算法笔记》以简洁清晰的语言、丰富的例子和详细的讲解,帮助读者掌握算法的基本原理和应用技巧。它不仅适用于想要参加编程竞赛的学生和程序员,也适用于对算法感兴趣的人士。通过阅读该书,读者能够系统地学习和应用算法,提高编程能力和算法思维能力。 总之,PAT晴神宝典是一本覆盖广泛且深入浅出的算法学习笔记,对于学习和应用算法的人士来说,它是一本十分有价值的资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值