PAT-图的遍历-1034 Head of a Gang

94 阅读2分钟

思路

  • 用hash的方法来映射每个人的名字,用邻接表存储节点
  • 每个点权在DFS中一次循环中相加ans+=adj[u][i].weight;从而超出头目。
  • 总边权把一个连通图的所有边相加除以二,因为重复计算了一次weight_total+=adj[u][i].weight 最后对名字进行字典序排序就完事了

注意

  • 学会结构体初始化函数。node(int _weight,int _v) :weight(_weight),v(_v) {}!!!!!
  • 熟练使用map和string会简单很多,和节省很多空间,代码二为使用map做法。!!!!!!
  • 十进制与其他进制的转换。!!!!!!
  • 数组不能直接做返回值,除非使用了new赋予了空间来返回指针 !!!!!!!!
  • 边权和点权的统计要在dfs递归之前,因为不考虑重复和环,否则要删除掉以访问的边,具体见代码二。
  • 可以建立两个map对名字和编号分别映射,见代码二。
  • map可以直接排序,非常简单。

代码一

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

int person,weight_total;
const int maxn=101000;     //要稍微大一点,映射完全部字符 
int N,K;                   
int vis[maxn]={0};      //标记数组 
int max_weight,max_v;   //头目的权值和标号 
struct node{  //节点 
	int weight;
	int v;
	node(int _weight,int _v) :weight(_weight),v(_v) {
	}
};
struct gang{  //帮派 
	char head[4];  //头目名 
	int person;   //人数 
}gangs[maxn];
vector<node> adj[maxn];  

bool cmp(gang a,gang b){  //比较头目的字典序 
	if(a.head[0]!=b.head[0]) return a.head[0]<b.head[0];
	else if(a.head[1]!=b.head[1]) return a.head[1]<b.head[1];
	else return a.head[2]<b.head[2];
}

int judge(int a,int b){   //判断是否有边,有返回在边表的位置 
	for(int i=0;i<adj[a].size();i++){
		if(adj[a][i].v==b) return i;
	}
	return -1;
}
int trans(char a[]){ //名字映射为对应的哈希值 
	int temp=0;
	for(int i=0;i<3;i++){
		temp=temp*26+a[i]-'A';
	}
	return temp;
}
void trans2(int n,char temp[]){  //哈希值映射回名字 
	temp[0]=n/26/26+'A';
	temp[1]=n%(26*26)/26+'A';
	temp[2]=n%26+'A';
	temp[3]='\0';
}

void DFS(int u){
	vis[u]=0;
	person++;
	int ans=0;
	for(int i=0;i<adj[u].size();i++){
		int v=adj[u][i].v;
		ans+=adj[u][i].weight;   //统计点权 
		weight_total+=adj[u][i].weight;  //统计总边权 
		if(vis[v]==1){ 
			DFS(v);
		}
	}
	if(ans>max_weight){         //找首领 
		max_v=u;
		max_weight=ans;
	}
}

int main(){
	scanf("%d%d",&N,&K);
	char v1[4],v2[4];
	int w;           
	for(int i=0;i<N;i++){        //建立图 
		scanf("%s %s %d",v1,v2,&w);      
		int x1=trans(v1);               
		int x2=trans(v2);
		adj[x1].push_back(node(w,x2));
		adj[x2].push_back(node(w,x1));
		vis[x1]=vis[x2]=1;         //标记为x1,x2存在于图中 
	}
	int up_limit=26*26*26;
	int count=0;
	for(int i=0;i<=up_limit;i++){
		if(vis[i]==1){
			max_v=max_weight=weight_total=person=0;      //分别是周围边权值最大定点和对应权值,帮派的总权值和人数
			DFS(i);
			if(weight_total/2>K&&person>2){    //所有边重复计算了一次,因此除以2 
				trans2(max_v,gangs[count].head);     
				gangs[count++].person=person;
			}
		}
	}
	printf("%d\n",count);
	sort(gangs,gangs+count,cmp);      //排序 
	for(int i=0;i<count;i++){
		printf("%s %d\n",gangs[i].head,gangs[i].person);
	}
	return 0;
}

代码二

#include<map>
#include<string>
#include<iostream>
const int maxn=2010; 
using namespace std;
map<int,string> intTostring;  //名字到编号 
map<string,int> stringToint;  //编号到名字 
map<string,int> gangs;        //名字
int G[maxn][maxn];
int weight[maxn];  //点权
int n,k,numPerson=0; 
bool vis[maxn]={false};

int change(string str){
	if(stringToint.find(str)!=stringToint.end()){
		return stringToint[str];
	}
	else {
		stringToint[str]=numPerson;
		intTostring[numPerson]=str;
		return numPerson++;
	}
}

void DFS(int u,int &head,int &numMember,int &totalValue){
	vis[u]=true;
	numMember++;
	if(weight[u]>weight[head]) head=u;
	for(int i=0;i<numPerson;i++){
		if(G[u][i]>0){
			totalValue+=G[u][i];
			G[u][i]=G[i][u]=0;
			if(vis[i]==false){
			DFS(i,head,numMember,totalValue);
			}
		}
	}
}

void DFSTrave(){
	for(int i=0;i<numPerson;i++){
		if(vis[i]==false){
			int head=i,numMember=0,totalValue=0;
			DFS(i,head,numMember,totalValue);
			if(numMember>2&&totalValue>k){
				gangs[intTostring[head]]=numMember;
			}
		}
	}
}

int main(){
	cin>>n>>k;
	int w;
	string str1,str2;
	for(int i=0;i<n;i++){
		cin>>str1>>str2>>w;
		int x1=change(str1);
		int x2=change(str2);
		weight[x1]+=w;
		weight[x2]+=w;
		G[x1][x2]+=w;
		G[x2][x1]+=w;
	}
	DFSTrave();
	cout<<gangs.size()<<endl;
	map<string,int>::iterator it;
	for(it=gangs.begin();it!=gangs.end();it++){
		cout<<it->first<<" "<<it->second<<endl;
	}
	return 0;
}