codeforces 976D Degree Set 双指针(未终测)

Revision en1, by LittleFall, 2018-04-30 20:56:58

题意

给定n(300)和n个数的数组d1,d2,...,dn(从小到大,不重复,di最大1000),要求构造一个简单无向图使得所有节点的度数组成的集合恰为这个数组d,图的节点数为dn+1(即图中至少有一个点与其他所有点有边连接),输出边数和所有边(节点从1到dn+1编号)

分析

题目实际上要求对集合中的每个值,都有某些点的度数为这个值.

把当前节点从1到dn+1编号,由小到大遍历 如果一条边是由遍历到当前节点时发出的,称为主动连接,否则称为被动连接 构造时,每个点如果可以进行主动连接,那么**就向它后面的所有点主动连接**,否则不进行连接. 举个例子,8个点的图,是否进行主动连接的bool值依次为 1 1 0 0 1 1 0 0 (最后一个点连不连实际无所谓) 它们的度数就分别是 7 7 2 2 4 4 4 4 遍历到某个点时它的被动连接数为 0 1 2 2 2 3 4 4 这样,每个点被动连接的个数会按节点编号逐渐增大

记录当前已被连接的个数ready=0,以及集合的前后双指针l=1,r=n, 遍历到每个点时依次判断三种情况 1.save[l]==ready 意思是被动连接的个数恰好等于当前左指针位置的值,此时不连接,l++,r-- 2.ready+总点数-当前点数==save[r] 意思是主动连接的个数+被动连接的个数恰好等于当前右指针位置的值,连接,ready++ 3.其他情况 此时被动连接的个数等于上一个左指针位置的值,不连接,不修改记录.

1号节点符合情况2.每个情况1都不会连续出现,(应该)可以证明集合中所有的值都会出现.

至于边数的问题,时间很宽裕,多算一遍就好.

代码

/* LittleFall : Hello! */
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read();
inline void write(int x);
const int M = 1024;
int save[M];

int main(void)
{
	int n=read(),ans=0;
	for(int i=1;i<=n;i++)
		save[i]=read();
	int vecs=save[n]+1;

	int r=n,l=1,ready=0;
	for(int vec=1;vec<vecs;vec++)
	{
		if(save[l]<=ready)
			l++,r--;	
		else if(ready+vecs-vec==save[r])
			ready++,ans+=vecs-vec;
	}
	printf("%d\n",ans );

	r=n,l=1,ready=0;
	for(int vec=1;vec<vecs;vec++)
	{
		if(save[l]<=ready)
			l++,r--;
		else if(ready+vecs-vec==save[r])
		{
			for(int j=vec+1;j<=vecs;j++)
				printf("%d %d\n",vec,j );
			ready++;
			ans+=vecs-vec;
		}
	}

	return 0;
}


inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en3 English LittleFall 2019-07-05 09:54:00 26
en2 English LittleFall 2018-05-01 14:45:44 810
en1 English LittleFall 2018-04-30 20:56:58 1827 Initial revision (published)