题解:CF1082D Maximum Diameter Graph
Aurelia_Veil · · 题解
题解:CF1082D Maximum Diameter Graph
可能是一种很直观、保险的做法,但是时间复杂度较劣。
首先,直径的端点一定是叶子,如果存在一个满足度数限制的连通图,那么它的任意一棵生成树仍然满足度数限制,且对于任意连通图,取其一棵生成树后,任意两点间的距离不会减小(因为删边可能使路径变长),因此生成树的直径不小于原图的直径,因此只需要考虑构造一棵树。我们可以从大到小枚举直径长度。为了使直径最大,我们应该让尽量多的点放在直径路径上,其余点作为叶子挂在直径路径上的点旁边作为枝叶。注意度数上界
- 从
a_i=1 的点中选出两个“端点”作为直径的两端(如果少于两个则用最小的a_i > 1 的结点替代正确性显然)。 - 在它们之间插入若干
a_i 尽量小的“内部点”构成一条长链。 - 其余所有点作为叶子,挂在链上已有的点上(不能挂在端点上否则会破坏答案反正你最先也枚举到这种可能)。
策略想出来了怎么判定无解呢?我们可以使用大根堆存储还可以连接的点,每次取出一个,将除直径以外的点连接上去如果还可以连接就把他放回去(除直径以外的点和堆中提取出的点都可以放回去),如果在一些点未连接上时堆空了则代表该直径长度无法构造出合法的树则跳过,去枚举更短的直径。为什么用大根堆?贪心策略,每次取出剩余可连度数最大的点,防止出现假无解情况。
建边就很简单了,在每次取出堆中元素并连接时就可以使用任意建边的数据结构建边,最后跑一次搜索就可以了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1111;
int n;
int a[N],b[N];
vector<int>q;//起终点(度1)
vector<int>p;//衔接点or非径点(度2及上)
vector<int>g[N];//树
bool cmp(int x,int y){
return a[x]<a[y];
}
bool _cmp(int x,int y){
return a[x]>a[y];
}
void dfs(int u,int dad){
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==dad){
continue;
}
printf("%d %d\n",u,v);
dfs(v,u);
}
}
int main(){
// freopen("diameter.in","r",stdin);
// freopen("diameter.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
if(a[i]==1){
q.push_back(i);
}else{
p.push_back(i);
}
}
sort(p.begin(),p.end(),_cmp);
if(q.size()<2){
while((!p.empty())&&q.size()<2){
q.push_back(p[p.size()-1]);
p.pop_back();
}
}
sort(p.begin(),p.end(),cmp);
sort(q.begin(),q.end(),_cmp);
priority_queue<pair<int,int>>dl;
for(int i=p.size();i>=0;i--){
for(int j=1;j<=n;j++){
a[j]=b[j];
g[j].clear();
}
int suml=0;
int sumr=0;
int last=q[0];
a[q[0]]--;
for(int j=0;j<i;j++){
// cerr<<last<<" "<<p[j]<<"\n";
g[last].push_back(p[j]);
g[p[j]].push_back(last);
a[p[j]]-=2;
if(a[p[j]]>0){
dl.push({a[p[j]],p[j]});
}
last=p[j];
}
g[last].push_back(q[1]);
g[q[1]].push_back(last);
a[q[1]]--;
bool flag=0;
for(int j=i;j<p.size();j++){
if(dl.empty()){
flag=1;
break;
}
int u=dl.top().second;
dl.pop();
// cerr<<u<<" "<<p[j]<<"\n";
g[u].push_back(p[j]);
g[p[j]].push_back(u);
a[u]--;
if(a[u]>0){
dl.push({a[u],u});
}
a[p[j]]--;
if(a[p[j]]>0){
dl.push({a[p[j]],p[j]});
}
}
for(int j=2;j<q.size();j++){
if(dl.empty()){
flag=1;
break;
}
int u=dl.top().second;
dl.pop();
g[u].push_back(q[j]);
g[q[j]].push_back(u);
a[u]--;
if(a[u]>0){
dl.push({a[u],u});
}
a[q[j]]--;
}
if(flag){
continue;
}
printf("YES %d\n%d\n",i+1,n-1);
dfs(1,0);
return 0;
}
printf("NO");
return 0;
} //