名人问题

题目:

      在一次聚会中,你想找出来聚会的人中所有的名人,名人就是别人认识他,而他不认识别人。现在在聚会的k个人中,输入邻接矩阵adj[i][j]用来表示他们之间的关系,其中   adj[i][j]=1表示i认识j,adj[i][j]=0表示i不认识j。

输入:   第一行输入k,表示一共有k个人,编号从1到n

            之后k行输入邻接矩阵

输出;   输出所有名人的编号,没有名人则输出-1


输入示例:                                                     输出示例

3                                                                   2

0 1 0

0 0 0

1 1 1


分析:拿到这题,最简单的方法就是对于每个人i,进行遍历,在i确定时,再遍历j,进行i和j之间的关系判断,最后找到名人,这个方法时间复杂度达到O(n^2),不是很有效率,这时我们就想能否找到一个时间复杂度只有O(n)的解决方案,答案是有的,而且不止一个,现在一一道来:

想要进行优化,我们首先要知道一个问题,名人最多只有一个,不可能同时存在两个名人,这是很容易想的,我就不过多废话,并且在adj[i][j]=0,我们知道i不认识j,那么根据名人的定义,j肯定不是,如果adj[i][j]=1,那么也很容易得到i不是名人,那么这样在无论adj[i][j]是0还是1的情况下,都能排除其中一个人不是名人,那么只需要在剩下的人中进行查找即可.

那么我们就可以将目前比较的人i,j保存在数组的前两个元素中,每次判断后都从将数组的最后一个人替换掉刚才不是名人的那个人,直到判断到只剩下一个人,那么再对那个人进行具体的判断,具体源码见下:

#include<iostream>
using namespace std;
#define Max 10000
int n;
int adj[Max][Max];
int findFamous()
{
	int person[Max];           // person存放还未判断是不是名人的数组 
	for(int i=0;i<n;i++)
	{
		person[i]=i;
	}
	int count=n;
	while(count>1)
	{
		if(adj[0][1])                    //第一个人认识第二个人,说明第一个人不是名人 
		  person[0]=person[--count];     //从 person中删除第一个人 
	    else
	      person[1]=person[--count];      //第二个人认识第一个人,说明第二个人不是名人 
		                                    //从 person中删除第二个人 
	}
	//剩下只有person[0]才有可能是名人,再进行完全判断一下
	for(int i;i<n;i++)
	{
		if(person[0]!=i && (adj[person[0]][i]==1 || adj[i][person[0]]==0))
		   return -1;
	} 
	return person[0]+1;
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		  cin>>adj[i][j];
	}
	
	cout<<findFamous()<<endl;
	return 0;
}

下面的一个方法和上面的思想类似,只是 i,j一开始分别指向数组的第一个人和最后一个人,在比较后,那一边的人不是名人,那么i或者j指针就分别向中间移动,直到最后i=j.
#include<iostream>
using namespace std;
#define Max 10000
int n;
int adj[Max][Max];
int findFamous()
{                                 //i,j的意义: 
	int i=0,j=1;                 //i,j表示有可能是名人的两个人 
	                            //且下标为0~i-1的人不是名人;下标i+1~j-1的不是名人 
	while(j<n)
	{
		if(adj[i][j])                    //i认识j,说明i不是名人,那么这时 标为0~i-1和下标i+1~j-1再加上 
		                                 //i不是名人,则0到j-1都不是名人,则i=j,这时还是满足i,j的意义 
		  i=j;     
	                                     //在i认识j时,直接j++,就行,i+1~j-1范围扩大一个
		j++;								 //但是这里要注意的是,即便i认识j,j还是要自加
										 //这时i,j意义还是满足的 
	}
	//i,j不断增大,最终j>=n超出下标范围,i表示最终有可能的名人 
	//剩下只有person[0]才有可能是名人,再进行完全判断一下
	for(int k=0;k<n;k++)
	{
		if(k!=i && (adj[i][k]==1 || adj[k][i]==0))
		   return -1;
	} 
	return i+1;
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		  cin>>adj[i][j];
	}
	
	cout<<findFamous()<<endl;
	return 0;
}

最后的一个方法则有点绕,i<j,且i,j的意义是:i,j表示有可能是名人的两个人 ,且下标为0~i-1的人不是名人;下标j+1~n-1的不是名人 ,具体的i,j的变化趋势见如下代码:
#include<iostream>
using namespace std;
#define Max 10000
int n;
int adj[Max][Max];
 
int findFamous()
{                                 //i,j的意义: 
	int i=0,j=n-1;                //i,j表示有可能是名人的两个人 
	                              //且下标为0~i-1的人不是名人;下标j+1~n-1的不是名人 
	while(i<j)
	{
		if(adj[i][j])                    //i认识j,说明i不是名人,那么这时 i++,则i左侧不是名人的范围扩大 
		  i++;     
	     else                            //在i认识j时,直接j--,就行,j+1~n-1范围扩大一个
		  j--;								 
	}
	//最终i=j,由逻辑上可以知道,最对只有一个名人,下标为i,则最终i左边0~i-1和i右边i+1~n-1都不是名人 
	for(int k=0;k<n;k++)
	{
		if(k!=i && (adj[i][k]==1 || adj[k][i]==0))
		   return -1;
	} 
	return i+1;
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		  cin>>adj[i][j];
	}
	
	cout<<findFamous()<<endl;
	return 0;
}

PS:上学后,事情多,争取每周两篇左右,每篇一道算法题目的分析~

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是使用遗传算法优化GBDT的示例代码,其中使用了Python的scikit-learn和DEAP库: ``` python import random import numpy as np from deap import algorithms, base, creator, tools from sklearn.ensemble import GradientBoostingRegressor from sklearn.metrics import mean_squared_error # 定义遗传算法参数 POPULATION_SIZE = 50 P_CROSSOVER = 0.9 P_MUTATION = 0.1 MAX_GENERATIONS = 50 HALL_OF_FAME_SIZE = 5 RANDOM_SEED = 42 NUM_ATTRIBUTES = 10 # 设置随机种子 random.seed(RANDOM_SEED) # 创建适应度和个体类 creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) creator.create("Individual", list, fitness=creator.FitnessMin) # 初始化工具箱 toolbox = base.Toolbox() # 定义属性生成函数 toolbox.register("attr_float", random.uniform, 0, 1) # 定义个体生成函数 toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, NUM_ATTRIBUTES) # 定义种群生成函数 toolbox.register("population", tools.initRepeat, list, toolbox.individual) # 定义评估函数 def evaluate(individual): # 将属性转换为GBDT参数 params = { 'n_estimators': int(individual[0] * 1000) + 100, 'max_depth': int(individual[1] * 5) + 1, 'min_samples_split': int(individual[2] * 10) + 2, 'learning_rate': individual[3] * 0.1 + 0.01, 'loss': 'ls', 'subsample': individual[4] * 0.5 + 0.5, 'max_features': None, 'random_state': 42 } # 训练GBDT并计算MSE X_train = np.random.rand(100, NUM_ATTRIBUTES) y_train = np.random.rand(100) X_test = np.random.rand(50, NUM_ATTRIBUTES) y_test = np.random.rand(50) clf = GradientBoostingRegressor(**params) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) mse = mean_squared_error(y_test, y_pred) return mse, # 注册评估函数 toolbox.register("evaluate", evaluate) # 注册交叉函数 toolbox.register("mate", tools.cxBlend, alpha=0.5) # 注册变异函数 toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=0.1, indpb=0.05) # 注册选择函数 toolbox.register("select", tools.selTournament, tournsize=3) # 创建种群和统计信息对象 population = toolbox.population(n=POPULATION_SIZE) hall_of_fame = tools.HallOfFame(HALL_OF_FAME_SIZE) stats = tools.Statistics(lambda ind: ind.fitness.values) stats.register("avg", np.mean) stats.register("std", np.std) stats.register("min", np.min) stats.register("max", np.max) # 运行遗传算法 population, logbook = algorithms.eaSimple(population, toolbox, cxpb=P_CROSSOVER, mutpb=P_MUTATION, ngen=MAX_GENERATIONS, stats=stats, halloffame=hall_of_fame, verbose=True) # 输出结果 best = hall_of_fame[0] params = { 'n_estimators': int(best[0] * 1000) + 100, 'max_depth': int(best[1] * 5) + 1, 'min_samples_split': int(best[2] * 10) + 2, 'learning_rate': best[3] * 0.1 + 0.01, 'loss': 'ls', 'subsample': best[4] * 0.5 + 0.5, 'max_features': None, 'random_state': 42 } clf = GradientBoostingRegressor(**params) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) mse = mean_squared_error(y_test, y_pred) print("MSE: %.3f" % mse) print("Best individual: ", best) ``` 在以上示例代码中,我们使用DEAP库来创建遗传算法,并使用scikit-learn的GradientBoostingRegressor作为待优化的模型。我们定义了10个属性来表示GBDT的参数,包括树的数量、最大深度、最小样本分割数、学习率、子采样率等。我们使用随机生成的数据进行训练和测试,并计算MSE作为适应度函数。在每一代中,我们使用交叉、变异和选择操作来更新种群,并保留一些最优的个体在名人堂中。最终,我们选择名人堂中最优的个体作为优化后的GBDT模型,并计算在测试集上的MSE。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值