트리 알고리즘을 스패닝하는 최소 분산을 개선하기 위해 (열거 + 크루스 칼)

트리 알고리즘에 걸친 질문은 최소 분산을 개선하기 위해

자원 제약 조건
시간 제한 : 1.0s 메모리 제한 : 256.0MB의
문제 설명
가중 무향 그래프 부여는 분산의 최소 스패닝 트리를 찾을 수 있습니다.
입력 형식의
테스트 데이터 입력 다중 세트. 첫 번째 행의 N, M은 포인트와 가장자리 하였다. 사이드, 다음 M 개의 행으로 오른쪽, U, V, W가이며, U 링킹을 나타내는 세 개의 정수의 각 행, V 값 W. 도 통신을 보장한다. N = m = 0마르크 테스트 파일의 끝.
출력 형식
0.01 반올림 각 시험 최소 출력 편차에 대해서는. 상기 출력 포맷에 따라 샘플.
샘플 입력
4. 5.
1 1 2.
2 2 3
3 4 2.
4. 1. 1.
2 4 3
4.6.
(1) (1) (2).
2 2 3
3 4 3.
4. 1. 1.
2 4 3
1 3 3.
0 0
샘플 출력
사례 1 :. 0.22
케이스 2 : 0.00
의 데이터 사이즈 및 규칙
1 <= U, V <= <= 50 W N <= 50, N-1 <= M <= 1000,0 <=. 5 개 그룹을 초과하지 않습니다.

해석의
개념 최소 분산 트리, 상기 N도 노드 픽 N-1면, 제 확인이도 통신하고 분산 인의 N-1의 최소 가중치의 가장자리의 편차를 산출 화학식이다 :
그림 삽입 설명 여기
알 수있는 바와 같이, 평균 관련 측면으로 분산되지만 어려움 측 첨가, 동적으로 변화하는 측면의 평균,도이다 것, 문제 해결에 도움이 아니다.
가지 아이디어는 다음 에지 웨이트의 합이 알려져 있기 때문에 쉽게 달성하는 공지 된 최소 차이에 에지의 총 중량을 해결하고, 또한 그 다음 공지 된 노드의 수 (N), 에지 개수 우리는 평균 제곱 양쪽이 이동하지 않는 상수 그래서, (N-1)이므로 에지의 평균값을 알고 있다는 것을 알고있다. 이 시점에서 우리는 다음 스패닝 트리 최소를 처리, 원래 무게의 가장자리 가중치가 아니라 그 무게와 평균 차이의 제곱 후 최소 스패닝 트리의 단계 최소 분산 결정에 따라 .
에서 설정 및 체크 코드에 사용 된 MST의 크루스 칼 알고리즘을 사용하여 다음과 같이 AC 코드는 https://www.cnblogs.com/asuml/p/6798307.html을

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

double const MAX = 10000000000000.0;
int n, m, tmp[1005], fa[55];
double ans;

struct Edge
{
    int u, v;
    double w, val;
}e[1005];

bool cmp(Edge a, Edge b)
{
    return a.w < b.w;
}

void UF_set(int n)
{
    for(int i = 1; i <= n; i++)
        fa[i] = i;
}

int Find(int x)
{
    return x == fa[x] ? x : fa[x] = Find(fa[x]);
}

void Union(int a, int b)
{
    int r1 = Find(a);
    int r2 = Find(b);
    if(r1 != r2)
        fa[r2] = r1;
}

void Kruskal(int sum)
{
    UF_set(n);
    int cnt = 0;
    double f_all = 0;
    double all = 0;
    double ave = sum * 1.0 / (n - 1);
    for(int i = 0; i < m; i++)
        e[i].w = (e[i].val - ave) * (e[i].val - ave);
    sort(e, e + m, cmp);
    for(int i = 0; i < m; i++)
    {
        int u = e[i].u;
        int v = e[i].v;
        if(Find(u) != Find(v))
        {
            Union(u, v);
            f_all += e[i].w;
            all += e[i].val;
            cnt ++;
        }
        if(cnt == n - 1)
            break;
    }
    if((int)all == sum)
        ans = min(ans, f_all);
}

int main()
{
    int ca = 1;
    while(scanf("%d %d", &n, &m) != EOF && (m + n))
    {
        // if(n == 1 || n == 2)
        // {
        //     printf("0.00\n");
        //     continue;
        // }
        int minv = 0;
        int maxv = 0;
        ans = MAX;
        for(int i = 0; i < m; i++)
        {
            scanf("%d %d %lf", &e[i].u, &e[i].v, &e[i].val);
            tmp[i] = e[i].val;
        }
        sort(tmp, tmp + m);
        for(int i = 0; i < n - 1; i++)
            minv += tmp[i];
        for(int i = m - 1; i > m - n; i--)
            maxv += tmp[i];
        for(int i = minv; i <= maxv; i++)
            Kruskal(i);
        ans = ans / (n - 1);
        printf("Case %d: %.2f\n", ca++, ans);
    }
}

알 수있는 바와 같이, 프레스 에지 가중치 열거 하강하고, N-1, N-1 최대 값이면 전방에서 촬영 에지가 최소 값을 전면에서 후면으로 취해진 측면을, MST가 발견되는 등이있는 경우 다음의 최소 스패닝 트리의 비용이 둘 사이에있을 것입니다, 그는에 거,이 경우 가장 작은 차이를 찾아, 최종에서 가장 작은 가능한 모든 분산을 촬영합니다.

PS. 편집자 연습, 아니 AC,이 약간의 기능 :, 또는 당신과 함께 공유 하시겠어요
, 우선 순위 큐, 트리 알고리즘에 걸친 꼼꼼한 최소를 사용하여 우리가 시작 측면으로 일측으로, 점 (A)를 선택하고, 다음 평균 차이마다 큐로부터 현재 선택된 평균 측 갱신 측면에서 촬영마다 최소한의 대기 측 액세스 알려진 모든 노드이다. 이것은 또한 내가 MST로 구성된 어느 쪽을 모르기 때문에, 출발의 다른 측면에서 통과해야하지만 확실히 가장자리가 MST, 또는 고립의 노드에 속해있다. 그리고 이러한 상황에서 분산의 최소값.
다음과 같이 코드입니다 :

#include<bits/stdc++.h>
using namespace std;

struct edge {
	int from;
	int to;
	int we;
	edge(int _from = 0, int _to = 0, int _we = 0) { from = _from; to = _to; we = _we; }
};
vector<edge> V[51];
vector<edge> choose;
bool vis[51];
int N, M;
float fenzi, fenmu, shang;
float ans;


struct cmp {
	bool operator()(edge a, edge b) {
		return  abs(a.we - shang) > abs(b.we - shang);
	}
};


void prim() {
	ans = 1e6;
	for (int i = 0; i < V[1].size(); i++) {

		priority_queue<edge, vector<edge>, cmp > Q;
		memset(vis, 0, sizeof(vis));
		fenzi = 0;
		fenmu = 0;
		choose.clear();

		int code = 1;
		Q.push(V[1][i]);
		vis[1] = 1;
		while (!Q.empty()) {
			edge tmp = Q.top();
			Q.pop();
			if (code) {
				code = 0;
				for (int j = 0; j < V[1].size(); j++)
					if (j != i)
						Q.push(V[1][j]);
			}
			if (vis[tmp.to])
				continue;

			vis[tmp.to] = 1;
			choose.push_back(tmp);
			fenmu++;
			fenzi += tmp.we;
			shang = fenzi / fenmu;

			for (int j = 0; j < V[tmp.to].size(); j++) {
				Q.push(V[tmp.to][j]);
			}

		}
		float h = 0;
		for (int j = 0; j < choose.size(); j++) {
			h += (choose[j].we - shang)*(choose[j].we - shang);
		}
		ans = min(ans, h / fenmu);
	}
}

int main() {
	int a, b, c;
	int ccase = 0;
	while (scanf("%d%d", &N, &M) && N&&M) {
		ccase++;
		for (int i = 0; i < 51; i++)
			V[i].clear();
		for (int i = 0; i < M; i++) {
			scanf("%d%d%d", &a, &b, &c);
			V[a].push_back(edge(a, b, c));
			V[b].push_back(edge(b, a, c));
		}

		prim();

		printf("Case %d: %.2f\n", ccase, ans);
	}
	return 0;
}
게시 23 개 원래 기사 · 원 찬양 2 · 조회수 1,212

추천

출처blog.csdn.net/Raymond_YP/article/details/104581098