1. 程式人生 > >CodeForces125E MST Company(根限度為k的最小生成樹+二分)

CodeForces125E MST Company(根限度為k的最小生成樹+二分)

The MST (Meaningless State Team) company won another tender for an important state reform in Berland.

There are n cities in Berland, some pairs of the cities are connected by roads. Each road has its price. One can move along any road in any direction. The MST team should carry out the repair works on some set of roads such that one can get from any city to any other one moving only along the repaired roads. Moreover, this set should contain exactly k

 capital roads (that is, the roads that start or finish in the capital). The number of the capital is 1.

As the budget has already been approved, the MST Company will profit by finding the set with minimum lengths of roads.

Input

The first input line contains three integers n, m, k (1 ≤ n ≤ 5000;0 ≤ m

 ≤ 105;0 ≤ k < 5000), where n is the number of cities in the country, m is the number of roads in the country, k is the number of capital roads in the required set. Then m lines enumerate the roads in question. Each road is specified by three numbers ai, bi, wi(1 ≤ ai, bi ≤ n1 ≤ w ≤ 105), where ai
, bi
 are the numbers of cities linked by a road and wi is its length.

Between each pair of cities no more than one road exists. There are no roads that start and finish in one city. The capital's number is 1.

Output

In the first line print the number of roads in the required set. The second line should contain the numbers of roads included in the sought set. If the sought set does not exist, print -1.

Example Input
4 5 2
1 2 1
2 3 1
3 4 1
1 3 3
1 4 2
Output
3
1 5 2 

題解:

題意:

給你n個點,m條邊,限度k,讓你求一個根(就是1號節點)度數為k的最小生成樹,輸出邊數和選擇邊的序號,如果沒有就輸出-1

思路:

我們用克魯斯卡爾演算法先跑一遍最小生成樹,看選擇的與1相連的邊數為多少,如果大於k的話我們就將所有與1相連的邊加上一個相同的權值mid,使得這些邊可以後一些選擇,否則就減去一個mid,使得這些邊前一些選擇,然後繼續跑一遍克魯斯卡爾,直到找到那個增量為止,因為這個增量加上以後可能有些與1相連的邊值相同,那麼選的與1相連的邊就會超過k,特判一下就行了。。。還有就是要用double來求增量,那麼判斷相等時就要用一個eps

程式碼:

#include<iostream>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<string>
#include<stdio.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<deque>
#include<algorithm>
using namespace std;
#define INF 100861111
#define ll long long
#define eps 1e-5
struct edge
{
    int f,t;
    double v;
    int id;
    friend bool operator<(edge x,edge y)
    {
        return x.v<y.v;
    }
}a[100005],b[100005];//a為排序後的原始陣列,b為與1相連的邊加上增量後的陣列
int p[10005];//存與1相連的邊的號
int n,m,k;
int choose[100005];//存選擇的邊的號
int pre[5005];
int find(int x)
{
    if(x!=pre[x])
        pre[x]=find(pre[x]);
    return pre[x];
}
int kl(int tag)//tag為0表示普通克魯斯卡爾,為1表示求與1相連的邊數為k的最小生成樹
{
    int i,j,d1,d2;
    sort(b+1,b+m+1);
    for(i=1;i<=n;i++)
        pre[i]=i;
    int ans=0,tot=0;
    for(i=1;i<=m;i++)
    {
        d1=find(b[i].f);
        d2=find(b[i].t);
        if(d1!=d2)
        {
            if(tag)//tag為1時加上特判
            {
                if(b[i].f==1&&ans>=k)
                    continue;
            }
            pre[d2]=d1;
            choose[tot]=b[i].id;
            tot++;
            if(b[i].f==1)
            {
                ans++;
            }
        }
        if(tot>=n-1)
            break;
    }
    return ans;
}
int main()
{
    int i,j,x,y,ans=0,u,v,s=0;
    double l,r,mid;
    memset(num,0,sizeof(num));
    scanf("%d%d%d",&n,&m,&k);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%lf",&a[i].f,&a[i].t,&a[i].v);
        a[i].id=i;
        if(a[i].f>a[i].t)
            swap(a[i].f,a[i].t);
        if(a[i].f==1)//記錄1的度數
            ans++;
    }
    if(ans<k||(n>1&&k==0)||m<n-1)//不符合的情況
    {
        printf("-1\n");
        return 0;
    }
    sort(a+1,a+m+1);
    for(i=1;i<=m;i++)
    {
        if(a[i].f==1)
        {
            p[s]=i;
            s++;
        }
    }
    l=-100000.0,r=100000.0;//設定二分左右邊界
    int t;
    while(r-l>eps)
    {
        mid=(l+r)/2.0;
        for(i=1;i<=m;i++)
        {
            b[i]=a[i];
        }
        for(i=0;i<s;i++)
        {
            b[p[i]].v+=mid;
        }
        t=kl(0);
        if(t<k)
            r=mid-1;
        else
        {
            if(fabs(l-mid)<eps)//排除一直迴圈的情況
                break;
            l=mid;
        }
    }
    for(i=1;i<=m;i++)
    {
        b[i]=a[i];
    }
    for(i=0;i<s;i++)
    {
        b[p[i]].v+=l;
    }
    t=kl(1);//找到增量後再跑一遍
    printf("%d\n",n-1);
    if(k!=0)
        printf("%d",choose[0]);
    for(i=1;i<n-1;i++)
        printf(" %d",choose[i]);
    if(k!=0)
    printf("\n");
    return 0;
}