„Algorithmenwettbewerb·Schnelle 300 Fragen“ Eine Frage pro Tag: „Die Farm vereinfachen“

Algorithmen-Wettbewerb: 300 schnelle Fragen “ erscheint 2024 und ist ein Hilfsübungsbuch zum „Algorithmen-Wettbewerb“ .
Alle Fragen werden im selbst erstellten OJ New Online Judge platziert .
Die Codes werden in drei Sprachen bereitgestellt: C/C++, Java und Python. Die Themen sind hauptsächlich Themen auf mittlerem bis niedrigem Niveau und eignen sich für Einsteiger und Fortgeschrittene.


Simplifying Farm “, Link: http://oj.ecustacm.cn/problem.php?id=1879

Beschreibung der Frage

[Problembeschreibung] Johns Farm kann als Diagramm betrachtet werden. Das Ackerland stellt die Eckpunkte im Diagramm dar, und die Feldstraßen stellen die Kanten im Diagramm dar. Jede Kante hat eine bestimmte Länge.
   John entdeckte, dass es auf seiner Farm bis zu drei gleich lange Wege gab.
   John möchte einige Wege entfernen, damit die Farm zu einem Baum wird und nur noch ein Weg zwischen den beiden Feldern besteht.
   John möchte die Farm als minimalen Spannbaum entwerfen, d. h. die Gesamtlänge der Farmstraßen ist am kürzesten.
   Bitte helfen Sie John, die Gesamtlänge des minimalen Spannbaums zu ermitteln, und berechnen Sie bitte, wie viele Arten von minimalen Spannbäumen es insgesamt gibt.
[Eingabeformat] Die erste Eingabezeile enthält zwei positive ganze Zahlen N und M, die die Anzahl der Punkte und Seiten darstellen (1 <= N <= 40.000; 1 <= M <= 100.000).
   Die nächsten M Zeilen enthalten in jeder Zeile drei ganze Zahlen ai, bi, ci, was darauf hinweist, dass es zwischen den Punkten ai und bi eine ungerichtete Kante der Länge ci gibt. (1 <= ai, bi <= N; 1 <= ci <= 1.000.000)
[Ausgabeformat] Die Ausgabezeile enthält zwei Ganzzahlen, die die Länge des minimalen Spannbaums bzw. die Anzahl des minimalen Spannbaums darstellen.
Finden Sie den Rest der Zahl 1.000.000.007.
[Eingabebeispiel]

4 5
1 2 1
3 4 1
1 3 2
1 4 2
2 3 2

【Ausgabebeispiel】

4 3

Antwort

   Es gibt zwei MST-Algorithmen (Minimum Spanning Tree): Kruskal und Prim. Kruskals Idee ist, gierig nach Kanten zu sein, „die kürzeste Kante muss auf MST liegen“; Prims Idee ist, gierig nach Punkten zu sein, „der nächste Nachbarpunkt muss auf MST liegen“.
   Die Besonderheiten bei der Beschreibung dieser Frage sind: (1) Es gibt höchstens drei Pfade (Kanten) mit gleicher Länge; (2) Berechnen Sie, wie viele Arten minimal aufspannender Bäume es insgesamt gibt. Der Fokus liegt auf den Kanten, daher wird der Kruskal-Algorithmus verwendet.
   Die Ausführungsschritte des Kruskal-Algorithmus sind: (1) Sortieren Sie die Kanten; (2) Beginnen Sie mit der kürzesten Kante und fügen Sie die Kanten in der Reihenfolge von klein nach groß zum MST hinzu; (3) Verwenden Sie beim Hinzufügen von Kanten die Vereinigung Suchen Sie nach, um festzustellen, ob ein Zyklus generiert wird. Wenn ein Zyklus gebildet wird, verwerfen Sie diese Kante. (4) Ende, wenn alle Kanten verarbeitet wurden, oder beenden, wenn die Anzahl der hinzugefügten Kanten gleich n-1 ist.
   Wenn alle Kantenlängen unterschiedlich sind, gibt es nur einen minimalen Spannbaum. In der Frage heißt es: „Es gibt höchstens drei Seiten gleicher Länge.“ Aus dem Beispiel können wir ersehen, dass es zwei Seiten gleicher Länge und auch drei Seiten gleicher Länge gibt. Wenn Sie Kanten sortieren, liegen diese gleichen Kanten nebeneinander.
   Um mit Kanten gleicher Länge umzugehen, sei cnt die Anzahl der zulässigen Kanten (sogenannte legal bedeutet, dass diese Kante zu MST hinzugefügt wird, ohne einen Zyklus zu verursachen), und num ist die Anzahl dieser Kanten gleicher Länge, die hinzugefügt werden können zu MST gleichzeitig. Summe ist die Anzahl der minimalen Spannbäume.
   (1) Es gibt zwei gleich lange Seiten.
   Wenn cnt=1, ist nur eine Kante zulässig, das heißt, diese Kante hat keine Wahl, dann bleibt die Summe unverändert.
   Wenn cnt=2, gibt es zwei zulässige Kanten. Lassen Sie uns mit der Diskussion fortfahren:
   1) num=1, das heißt, nur eine dieser beiden Kanten gleicher Länge kann zu MST hinzugefügt werden. Dann ist sum = sum*cnt, also sum=sum*2. Nehmen Sie die folgende Abbildung als Beispiel. s1 und s2 sind zwei Teilbäume, die zu MST hinzugefügt wurden. In ihnen befinden sich keine Kreise. Fügen Sie nun zwei Seiten (x1, y1) und (x2, y2) gleicher Länge hinzu. Es ist zulässig, sie einzeln zu MST hinzuzufügen, aber wenn Sie sie gleichzeitig hinzufügen, entsteht ein Kreis.
Fügen Sie hier eine Bildbeschreibung ein
   2) num = 2, das heißt, diese beiden Seiten gleicher Länge sollten zu MST hinzugefügt werden. Dann bleibt die Summe unverändert, also sum=sum*1. Die folgende Abbildung ist ein Beispiel.
Fügen Sie hier eine Bildbeschreibung ein
   (2) Es gibt drei gleich lange Seiten.
   Wenn cnt=1, ist nur eine Kante zulässig und die Summe bleibt unverändert.
   Wenn cnt=2, gibt es zwei zulässige Seiten, was dasselbe ist wie (1), wenn zwei Seiten gleicher Länge vorhanden sind und cnt=2.
   Wenn cnt=3, gibt es drei zulässige Kanten, dann:
   1) num = 1, nur eine Kante kann zu MST hinzugefügt werden, sum = sum*cnt=sum*3. In der folgenden Abbildung gibt es beispielsweise drei Situationen, in denen Sie eine der drei Kanten auswählen können.
Fügen Sie hier eine Bildbeschreibung ein
   2) num = 2, es gibt zwei Kanten, die zu MST hinzugefügt werden können, und eine der Kanten muss hinzugefügt werden, sum = sum*2. In der folgenden Abbildung können Sie beispielsweise zwei der drei Kanten auswählen, und es gibt zwei Situationen.
Fügen Sie hier eine Bildbeschreibung ein
   3) num = 2, es gibt zwei Kanten, die zu MST hinzugefügt werden können, und es sind zwei beliebige Kanten, sum = sum*3. In der folgenden Abbildung können Sie beispielsweise zwei der drei Kanten auswählen und es gibt drei Situationen.
Fügen Sie hier eine Bildbeschreibung ein
   3) num = 3, alle drei Kanten sollten zu MST addiert werden und die Summe bleibt unverändert.

【Wichtige Punkte】 Kruskal.

C++-Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
const int mod = 1e9+7;
struct Node{
    
    int x,y,val;}e[N<<1];
bool cmp(Node a,Node b){
    
    return a.val<b.val;}//按边权排序
int n,m;
int s[N];          //并查集
int ans=0,sum=1;   //ans: MST的总长度, sum:最小生成树的数目
int find_set(int x){
    
                             //查询并查集,返回x的根
    if(x != s[x]) s[x] = find_set(s[x]);     //路径压缩
    return s[x];
}
void kruscal(){
    
    
	for(int i=1;i<=n;i++) s[i] = i;  //并查集初始化
	sort(e+1,e+m+1,cmp);             //边:升序排序
	for(int i=1;i<=m;){
    
                  //遍历所有边,每次处理其中的等长边
		int cnt = 0;                 //这次的等长边中,有几个可以加入MST
		set<pair<int,int> >st;       //set用于存储并去重
		int j;                       //第i~j个边等长
		for(j=i;j<=i+2 && e[i].val==e[j].val;j++){
    
     //枚举等长边,最多3个相同。更新j
			int s1 = find_set(e[j].x);   //边的一个端点属于哪个集?
			int s2 = find_set(e[j].y);   //边的另一个端点属于哪个集?
			if(s1 > s2)  swap(s1,s2);
			if(s1 != s2){
    
                    //两个集不等,这个边可以加入到MST中
				cnt ++;                  //cnt: 允许加入MST的边的数量
				st.insert(make_pair(s1,s2));   //这个边的两端点所属的集存到st中
			}
		}                                //第i~j个边是等长的
		int num = 0;
		for(;i<j;i++){
    
                       //开始时第i~j个边是等长的。i=j时退出
			int s1 = find_set(e[i].x);
			int s2 = find_set(e[i].y);
			if(s1 != s2){
    
                    //不属于一个集,可以加入到树里
				s[s2] = s1;              //并查集合并
				num++;                   //这几个等长边有num个可以同时加入树
			}
		}
		ans += e[i-1].val*num;    //这几个等长边最后有num个可以加入到MST,计算MST总长
		if(num == 1)  sum = sum*cnt%mod;   //只有一条边能加入树,直接乘以cnt
		if(cnt == 3 && num==2 && st.size() == 2) sum = 2*sum%mod;
		if(cnt == 3 && num==2 && st.size() == 3) sum = 3*sum%mod;
		 //其他情况方案数都不变
	}
}
signed main(){
    
    
	scanf("%lld%lld",&n,&m);  //读点,边
	for(int i=1;i<=m;i++)     //存边,用最简单的“边集数组”存边
		scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].val);
	kruscal();                //最小生成树
	printf("%lld %lld\n",ans,sum);
}

Java-Code

 

Python-Code

  

 

Supongo que te gusta

Origin blog.csdn.net/weixin_43914593/article/details/132629709
Recomendado
Clasificación