444. 爱好和平
时间限制 1000 ms
内存限制 65536 KB
题目描述
在星际时代,每个帝国都靠着贸易路线连接着各个联盟星球,这些贸易路线都是双向可达的。一个帝国的综合实力由他贸易连接着的联盟星球数决定。
学姐作为Mays帝国的领袖,长期与Luke帝国保持着敌对关系,爱好和平的学姐希望结束长达几个世纪的战争,于是找实验室定做了一颗代号小苹果的炸弹,可以定点摧毁一颗星球,这颗星球被毁后,与它相连的全部贸易就都被切断了,这样Luke帝国可能就被切断为一个小联盟,他们就再也不会对学姐的地位构成威胁啦~
经过调查,Luke帝国为了节约经费,他的联盟星之间都有且仅有一条直接或间接的贸易通路。
现在给出Luke帝国的贸易线路,学姐想知道摧毁哪一颗行星可以使得分裂后的若干Luke联盟威胁最大的分部最小。
输入格式
输入有多组数据,组数不大于10组。每一组开头一行为n,m,表示Luke帝国的联盟星球数量,和贸易关系数,接下来m行,每行两个整数u,v,表示星球u,v之间存在直接的贸易路线,1<=u,v<=n,1<=n,m<=100000
输出格式
输出一个数表示推荐学姐摧毁的星球,如果有多解,输出编号最小的一个。
输入样例
5 4
1 2
1 3
1 4
4 5
输出样例
1
首先是分析题意,根据题意可知目标是求每个节点所连接的(两个子节点和的最大数量)与(其父亲节点所连的另一边的节点数)的最大值dp[u],每个节点都得到这样的一个dp[u]值,然后遍历比较找到所有点的max值的最小值,然后选取该点。
包括知识点:用数组模拟邻接表+dfs
<span style="font-size:14px;">#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int maxn=100005; const int maxe=200010; int head[maxn]; int n,m; int u,v; int dp[maxn]; int num[maxn]; int cnt=1; struct { int to,next; }e[maxe]; void init() { for(int i=0;i<=n;i++) { head[i]=0; dp[i]=0; num[i]=0; } } void add(int u,int v) { e[cnt].to=v;//第cnt条边; e[cnt].next=head[u];//head数组的相当于下标的作用;而数组的下标u表示以u为起点; head[u]=cnt++; } int dfs(int u,int fa)//搜索是从u点出发的,若u连有x条边就dfs一次 { num[u]=1; for(int i=head[u];i!=0;i=e[i].next)//注意循环条件,head的值是cnt赋给的所以head也是相当于技术,若其等于0,表示最初的状态,即以该点为起始点的边已经遍历完毕; { int v=e[i].to; if(v==fa) continue;<span style="font-family: Arial, Helvetica, sans-serif;">//直到找到指向fa为止;</span> else num[u]+=dfs(v,u);//若没有找到fa则再往下继续搜索,每一次dfs都是搜一个点的所有儿子,所以dfs一次的返回值是儿子数目。 } dp[u]=n-num[u];//除了他和所有儿子以外剩的; for(int i=head[u];i!=0;i=e[i].next)//再遍历一遍; { int v=e[i].to; if(v==fa) continue; else dp[u]=max(num[v],dp[u]); } return num[u]; } int main() { while (scanf("%d%d",&n,&m)==2) { init(); cnt=1; for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1,0); int ans=10000000; for(int i=1;i<=n;i++) ans=min(ans,dp[i]); for(int i=1;i<=n;i++) { if(ans==dp[i]) { printf("%d\n",i); break; } } } return 0; }</span>
网上看来的,讲的挺好,其实后来发现这个其实数组的名字就是指针的概念,例如: next[i]=first[ u [ i ] ]
就是i->next=u[i]->first.
这个可以用来读入有向图(最好是稀疏)的边列表,然后建立邻接表。
- int main()
- {
- int i,j,n,m,t,c;
- int first[MAXN];
- int u[MAXN],v[MAXN],w[MAXN],next[MAXN];
- scanf("%d%d",&n,&m);
- for(i=0;i<=n;i++) first[i]=-1;
- for(i=0;i<m;i++)
- {
- scanf("%d%d",&u[i],&v[i]);//u是弧尾,v弧头
- next[i]=first[u[i]];//就这万恶的两行
- first[u[i]]=i;//短小精悍!
- }
- //后面为自己理解后添加的。
- for(i=1;i<=n;i++)
- {
- if(first[i]==-1)
- {
- printf("没有以点<%d>为起点的邻接表。\n",i);
- continue;
- }
- printf("以点<%d>为起点的邻接表:\n",i);
- for(j=first[i];j!=-1;j=next[j])
- {
- printf("<%d %d> ",i,v[j]);
- }
- printf("\n\n");
- }
- return 0;
- }
next[i]=first[ u [ i ] ] 表示第i条边的下一条边是first[ u [ i ] ];
first[ u [ i ] ]=i 就是把节点u[i] 的链表头指向第i条边。