PAT——图专题

1013 Battle Over Cities (25 分)

题目:n个城市(1到n),m条路,k个询问的城市,问当某城被攻陷时,若使其它各城连通,至少要修几条路

思路:vis[i]标识该城是否访问,若被攻陷则置为1遍历1到n,若不为1则dfs——标记访问与其联通的点计算连通分量,最少需要修的道路的数量为连通分量-1

注意:cin、cout会超时

  • 用scanf、printf
  • ios::sync_with_stdio(false)     (synchronize同步)
#include<iostream>
#include<cstring>
using namespace std;
int vis[1005],mp[1005][1005];
int n;
void dfs(int x) {
	vis[x]=1;
	for(int i=1; i<=n; i++)
		if(vis[i]==0&&mp[x][i]==1)
			dfs(i);
}
int main() {
	ios::sync_with_stdio(false);  //神奇的不超时办法!!!
	int m,k,cnt;
	cin>>n>>m>>k;
	int a,b,x;
	for(int i=0; i<m; i++) {
		cin>>a>>b;
		mp[a][b]=mp[b][a]=1;
	}
	for(int i=0; i<k; i++) {
		memset(vis,0,sizeof vis);
		cnt=0;
		cin>>x;
		vis[x]=1;
		for(int i=1; i<=n; i++) {
			if(vis[i]==0) {
				dfs(i);
				cnt++;
			}
		}
		cout<<cnt-1<<endl;
	}
	return 0;
}

1021 Deepest Root (25 分)

题意:n个点,n-1条边,若为树,输出使树达到最大高度的根节点

思路

  • dfs 判断连通分量个数,不为1,直接输出
  • 从任一节点遍历得到最大高度的节点们set1,从set1中任一节点开始,遍历得到最大高度的节点们set2,二者并集即为所求

步骤

  • 点多边少——邻接矩阵vector<int> v[maxn]
  • 遍历-->未标记dfs-->第一次且得到节点不为空存进set,第一个节点保存为根节点
  • cnt不唯一直接输出
  • 否则从根节点遍历得到tmp存入set
  • set自动从小到大排列,直接输出
#include<iostream>
#include<vector>
#include<set>
using namespace std;
int n,maxh=0;
int vis[10005];
vector<int> v[10005],tmp;
set<int> ans;
void dfs(int x,int h) {
	vis[x]=1;
	if(h>maxh) {
		maxh=h;
		tmp.clear();
		tmp.push_back(x);
	} else if(h==maxh)
		tmp.push_back(x);
	for(int i=0; i<v[x].size(); i++)
		if(vis[v[x][i]]==0) //
			dfs(v[x][i],h+1);
}
int main() {
	ios::sync_with_stdio(false);
	int a,b,cnt=0,root;
	cin>>n;
	for(int i=1; i<n; i++) {
		cin>>a>>b;
		v[a].push_back(b);
		v[b].push_back(a);
	}
	for(int i=1; i<=n; i++) {
		if(vis[i]==0) {
			dfs(i,1);
			cnt++;
			if(i==1) {
				if(tmp.size()) {
					root=tmp[0];
					for(int i=0; i<tmp.size(); i++)
						ans.insert(tmp[i]);
				}
			}
		}
	}
	if(cnt>1) printf("Error: %d components\n",cnt);
	else {
		fill(vis,vis+10005,0);
		tmp.clear();
		dfs(root,1);
		for(int i=0; i<tmp.size(); i++)
			ans.insert(tmp[i]);
		//set<int>::iterator it=ans.begin();
		for(auto it=ans.begin(); it!=ans.end(); it++) printf("%d\n",*it);
	}
	return 0;
}

1034 Head of a Gang (30 分)

题意:n条通话记录,标准时间k,找出团伙个数(总时间>k,人数>2)及每个团伙头目(打电话时间最长的那个)和成员个数

思路:dfs求连通分量+判断

步骤

  • n条记录最多2*n个人
  • 两个map将name与num对应标记
  • 个人times输入时就累加记录(日后用于选出头目)
  • 遍历dfsMap-->未标记dfs,判断当前团伙是否符合条件
  • dfs:cnt++标记,时刻更新头目,累加总时长,mp[x][i]置零(避免环),若未标记dfs,引用直接返回cnt和total用于比较
#include<iostream>
#include<vector>
#include<set>
#include<map>
using namespace std;
int n,k,pos=1;
int vis[2005],times[2005],mp[2005][2005];
map<string,int> num;
map<int,string> name;
map<string,int> ans;
int getId(string s){
	if(num[s]==0){
		num[s]=pos;
		name[pos]=s;
		return pos++;
	}else return num[s];
}
void dfs(int x,int &head,int &cnt,int &total){
	cnt++;
	vis[x]=1;
	if(times[x]>times[head]) head=x;
	for(int i=1;i<=n;i++){
		if(mp[x][i]!=0){
			total+=mp[x][i];
			mp[x][i]=mp[i][x]=0;
			if(!vis[i]) dfs(i,head,cnt,total);
		}
	}
}
void dfsMap(){
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			int cnt=0,head=i,total=0;
			dfs(i,head,cnt,total);
			if(cnt>2&&total>k) ans[name[head]]=cnt;
		}
	}
}
int main() {
	cin>>n>>k;
	string s1,s2;
	int a,b,t;
	for(int i=1;i<=n;i++){
		cin>>s1>>s2>>t;
		a=getId(s1),b=getId(s2);
		mp[a][b]+=t;
		mp[b][a]+=t;
		times[a]+=t;
		times[b]+=t;
	}
	dfsMap();
	cout<<ans.size()<<endl;
	for(auto it=ans.begin();it!=ans.end();it++) 
		cout<<it->first<<' '<<it->second<<endl;
	return 0;
}

1072 Gas Station (30 分)

题意:n个房子,m个加气站,k加气站最大覆盖范围。要求找出一加气站,使其覆盖所有房子,且距房子的最小距离最大,若多个解,输出平均值最小的那个,若还相同输出加气站下标最小的那个

思路:对m个加气站依次dijkstra,比较求最优解

步骤

  • 房子序号1到n,加气站序号n+1到n+m (1000+10)
  • 从n+1到n+m依次dis[id]=0,  Dijkstra(vis[i]!=0)
  • 1到n:dis[i]>ds mind=-1 break 否则mind=inf(dis[i]中取最小),avg=0(累加dis[i]
  •  ansd=-1(mind中取最大),ansavg=inf(avg中取最小
#include<iostream>
#include<string>
using namespace std;
const int inf=99999999;
int dis[1015],vis[1015],mp[1015][1015];
int n,m,k,ds;
int getId(string s) {
	if(isdigit(s[0])) return stoi(s);
	return n+stoi(s.substr(1));
}
int main() {
	fill(mp[0],mp[0]+1015*1015,inf);
	fill(dis,dis+1015,inf);
	cin>>n>>m>>k>>ds;
	string s1,s2;
	int a,b,l;
	for(int i=0; i<k; i++) {
		cin>>s1>>s2>>l;
		a=getId(s1);
		b=getId(s2);
		mp[a][b]=mp[b][a]=l;
	}
	int ansid=-1;
	double ansd=-1,ansavg=inf;
	for(int id=n+1; id<=n+m; id++) {
		fill(dis,dis+1015,inf);
		fill(vis,vis+1015,0);
		double mind=inf,avg=0;
		dis[id] = 0; //!!!		
		
		for(int i=1; i<=m+n; i++) { //dijkstra
			int minn=inf,u=-1;
			for(int j=1; j<=n+m; j++)
				if(!vis[j]&&dis[j]<minn)// vis
					minn=dis[j],u=j;
			if(u==-1) break;
			vis[u]=1;
			for(int j=1; j<=n+m; j++) {
				if(!vis[j]&&mp[u][j]!=inf) {
					if(dis[j]>dis[u]+mp[u][j])
						dis[j]=dis[u]+mp[u][j];
				}
			}
		}

		for(int i=1; i<=n; i++) {
			if(dis[i]>ds) {
				mind=-1;
				break;
			}
			if(dis[i]<mind) mind=dis[i];
			avg+=1.0*dis[i];
		}
		
		if(mind==-1) continue;
		avg/=n;
		if(mind>ansd) {
			ansd=mind;
			ansid=id;
			ansavg=avg;
		} else if(mind==ansd&&avg<ansavg) {
			ansid=id;
			ansavg=avg;
		}
	}
	if(ansid==-1) cout<<"No Solution\n";
	else printf("G%d\n%.1f %.1f\n",ansid-n,ansd,ansavg);
	return 0;
}

1076 Forwards on Weibo (30 分)

题意:有n个用户(序号从1到n),最大层数为l,给出每个用户关注的人数及具体的ID,给出k个询问,问id在l层内(包括l)最多有多少个关注她的人。

注意:dfs有两个点过不去,bfs就过了(bfs注意pop())

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
int n,l;
int vis[1005];
vector<int> vc[1005];
struct node {
	int id;
	int lev;
} a[1005];
void bfs(int root) {
	int cnt=0;
	node t,r;
	queue<node>q;
	q.push(node {root,0});
	while(!q.empty()) {
		t=q.front();
		q.pop();//!!!
		for(int i=0; i<vc[t.id].size(); i++) {
			int p=vc[t.id][i];
			r=node {p,t.lev+1};
			if(vis[p]==0&&r.lev<=l) {
				vis[p]=1;
				cnt++;
				q.push(r);
				//cout<<"level:"<<r.lev<<" point: "<<r.id<<endl;
			}
			if(r.lev>l) break;
		}
	}
	cout<<cnt<<endl;
}
int main() {
	ios::sync_with_stdio(false);
	cin>>n>>l;
	int k,x;
	for(int i=1; i<=n; i++) {
		cin>>k;
		for(int j=1; j<=k; j++) {
			cin>>x;
			vc[x].push_back(i);
		}
	}
	cin>>k;
	for(int i=0; i<k; i++) {
		cin>>x;
		fill(vis,vis+1005,0);
		vis[x]=1;
		bfs(x);
		//cout<<"this is "<<i+1<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值