Educational Codeforces Round 55 (Rated for Div. 2):D. Maximum Diameter Graph

D. Maximum Diameter Graph

题目链接https://codeforces.com/contest/1082/problem/D

题意:

给出n个点的最大入度数,要求添加边构成一个无环无向无重边的图,并且最大化图中的最短路径。

题解:

要求最短路径最大,我们会想到把每一个允许入度可以大于1的点先连起来,形成一个”链“,当前肯定满足条件,并且两端点的最短路径长度是所有情况中最大的。

然后对于入度为1的点,先尽量考虑放在链的两端,然后再在中间随便插入。

代码如下:

先附上自己用邻接矩阵存储的丑代码。。。代码思路比较直接:

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

const int N = 505;
int Map[N][N],vis[N],a[N];
int n;

int main(){
    scanf("%d",&n);
    int cnt = 0,tot = 0,st = 0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]==1) cnt++;
        else st=i;
    }
    vis[st]=1;bool flag=false;
    int end=st;
    for(int i=st;;i=end){
        if(flag) break;
        int tmp=0;
        for(int j=1;j<=n;j++){
            if(a[j]>1 && !vis[j]){
                tmp++;
                Map[i][j]=Map[j][i]=1;
                vis[j]=1;
                a[i]--;a[j]--;
                end=j;
                break;
            }
        }
        if(!tmp) flag=true;
    }
    int tmp = 0;
    for(int i=1;i<=n;i++) if(vis[i]) tot+=a[i],tmp++;
    if(cnt>tot){
        cout<<"NO";
        return 0;
    }
    int ans,edges=tmp-1;
    if(cnt>1) ans=edges+2;
    else if(cnt==1) ans=edges+1;
    else ans=edges;
    cout<<"YES"<<" "<<ans<<endl;
    if(cnt){
        cnt--;
        for(int i=1;i<=n;i++) if(!vis[i]){
            vis[i]=Map[i][st]=Map[st][i]=1;
            edges++;
            a[st]--;a[i]--;
            break ;
        }
        if(cnt){
            cnt--;
            for(int i=1;i<=n;i++) if(!vis[i]){
                vis[i]=Map[i][end]=Map[end][i]=1;
                edges++;
                a[end]--;a[i]--;
                break;
            }
        }
        if(cnt){
            for(int i=n;i>=1;i--) if(!vis[i]){
                vis[i]=1;
                for(int j=1;j<=n;j++){
                    if(a[j] && vis[j] &&j!=i){
                        edges++;
                        Map[j][i]=Map[i][j]=1;
                        a[j]--;a[i]--;
                        break;
                    }
                }
            }
        }
    }
    cout<<edges<<endl;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(Map[i][j]) printf("%d %d
",i,j);
    return 0;
}
View Code

再来一个O(N)的算法,注意一下代码的细节,直接像链一样连边:

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

const int N = 505;
int a[N];
int n,sum;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
    if(sum<2*(n-1)){
        cout<<"NO";return 0;
    }
    vector <int> ones;
    for(int i=1;i<=n;i++){
        if(a[i]==1) a[i]=0 , ones.push_back(i);
    }
    int t=ones.size();
    printf("YES %d
%d
",n-t-1+min(t,2),n-1);
    int st=0;
    if(!ones.empty()){
        st=ones.back();
        ones.pop_back();
    }
    for(int i=1;i<=n;i++){//从起点开始添边形成一个链
        if(a[i]>1){
            if(st){
                a[st]--;a[i]--;
                printf("%d %d
",st,i);
            }
            st=i;
        }
    }
    for(int i=n;i>=1;i--){ // 从尾开始添加(在链上) 
        while(a[i]>0 && !ones.empty()){
            a[i]--;
            int now1 = ones.back();ones.pop_back();
            printf("%d %d
",i,now1);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/10056152.html