Codeforces 1082D (贪心)

题面

传送门

分析

贪心

将度限制大于1的点连成一条链,然后将度限制等于1的点挂上去

形状如下图,其中(1,2,3)为度数限制>1的点

5c063524a9784.png

显然直径长度=(度数限制>1的节点个数)-1+min(度数限制等于1的节点个数,2)

那么具体如何构造?

首先将度为1和度>1的节点分开

如果有至少1个度为1的节点,就把它作为直径的起点,如图中的4

然后再将度>1的节点全部接上去

再从另一头倒着挂(图中3开始向2,1)剩下的度为1的节点

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 10005
using namespace std;
int n;
int a[maxn];
struct edge{
    int from;
    int to;
    edge(){
        
    }
    edge(int u,int v){
        from=u;
        to=v;
    }
    void print(){
        printf("%d %d\n",from,to);
    }
};
vector<edge>ans;
vector<int>l;
int main(){
    int leaf=0;
    scanf("%d",&n);
    int sum=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum+=a[i];
        if(a[i]==1) leaf++;
        if(a[i]==0){
            printf("NO\n");
            return 0;
        }
    }
    if(sum<n*2-2){
        printf("NO\n");
        return 0;
    }
    
    for(int i=1;i<=n;i++){
        if(a[i]==1){
            l.push_back(i);
            a[i]=0;
        }
    }
    
    
    int begin=-1;
    if(l.size()){
        begin=l[l.size()-1];
        l.pop_back(); 
    }
    for(int i=1;i<=n;i++){
        if(a[i]>1){
            if(begin!=-1){
                a[begin]--;
                a[i]--;
                ans.push_back(edge(begin,i)); 
            }
            begin=i;
        }
    }
    int t=0;
    for(int i=n;i>=1;i--){
        while(l.size()&&a[i]>0){
            a[i]--;
            ans.push_back(edge(i,l[t++]));
            if(t>=l.size()) break;
        }
        if(t>=l.size()) break;
    }
    
    int d=(n-leaf)-1+min(2,leaf);
    if(ans.size()==n-1){
        printf("YES %d\n%d\n",d,n-1);
        for(int i=0;i<n-1;i++){
            ans[i].print();
        }
    }else{
        printf("NO\n");
    }
}

转载于:https://www.cnblogs.com/birchtree/p/10064767.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值