名人问题

来源:互联网 发布:库存销售软件 编辑:程序博客网 时间:2024/04/27 15:03

题目:

      在一次聚会中,你想找出来聚会的人中所有的名人,名人就是别人认识他,而他不认识别人。现在在聚会的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 10000int 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 10000int 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 10000int 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:上学后,事情多,争取每周两篇左右,每篇一道算法题目的分析~

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子不爱跟外人说话怎么办 别人说我不说话怎么办 衣服屁股那块变亮怎么办 小孩家里说话外面不说话怎么办 2个月小宝宝便秘怎么办 3个月小宝宝便秘怎么办 微信客户不说话怎么办 一岁半宝宝便秘肛裂怎么办 网恋现在都不说话了怎么办 宝宝便秘四天了怎么办 月子里小孩吐奶怎么办 月子里的小孩吐奶怎么办 婴儿吃多了吐奶怎么办 20个月孩子便秘怎么办 一岁宝宝肛裂怎么办 婴儿吃饱了吐奶怎么办 23天新生儿吐奶怎么办 婴儿吐奶舌苔白怎么办 宝宝吐奶酸臭味怎么办? 1周岁吐奶有酸味怎么办 十多天的宝宝吐奶怎么办 未满月婴儿吐奶怎么办 2个月宝宝溢奶怎么办 四岁宝宝说话结巴怎么办 小孩说话结巴打顿怎么办 2岁宝宝突然说话结巴怎么办 2岁宝宝突然结巴怎么办 幼儿舌头起泡牙龈出血怎么办 小孩长得太快怎么办 脑出血压着神经不会说话怎么办 四岁宝宝说话有点口吃怎么办 三岁宝宝有点口吃怎么办 3岁宝宝有点口吃怎么办 三岁宝宝说话有点口吃怎么办 六岁说话重复第一个字怎么办 宝贝烧到39.5度怎么办 宝贝39度不退烧怎么办 两岁多小儿突然变得口吃怎么办 百度两周岁宝宝口吃怎么办 2岁宝宝偶尔结巴怎么办 两岁宝宝说话磕巴怎么办