codeforces1343F Restore the Permutation by Sorted Segments

https://codeforces.com/problemset/problem/1343/F

感觉这种2400分的题还比较适合刷,一般要做个1个多2个小时,但是要在比赛的时候能做出来的话,应该要能再半小时之内做出来才行。

这题首先想到,r=2的时候l=1,那么能做开头的数字是哪些就有限制了,必须是长度为2的序列中的。

然后我们可以算一个dis[i][j]表示i离j最远距离的最小值是多少,如果一个[l,r]长度是len,那么他们中间的元素之间相差的距离最多是len-1了,很多个区间中出线过,那就取最小值。所以直接枚举每一个开头st,看以这个数字开头能不能构造出合法序列,然后枚举每一个位置i,再找出之前已经填了的位置中数字当前剩下的数字中离他们最远距离最小的是哪个,然后取个最小的填到当前位置i。

然后这样尝试后发现过了样例,然而交上去又wa3由wa4

结果还是没想清楚,看了数据以后才明白,关键是由于每个区间r是独立的,也就是对于[l1,r-1]转移到[l2,r]这个区间时,从[l2,r-1]这个区间的数字必须把ans[r]这个数字给确定放在r这个位置才行,那么每次取出最远距离最小的必须等于i,才是合法序列

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

const int maxl=210;

int n,m,cas,cnt,tot;
int a[maxl][maxl],len[maxl],ans[maxl];
int dis[maxl][maxl];
char s[maxl];
bool in[maxl],vis[maxl]; 
struct node
{
	int dis,id;
	bool operator > (const node &b)const
	{
		return dis>b.dis;
	}
}mi[maxl];
priority_queue<node,vector<node>,greater<node>> q[maxl];

inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{	
		for(int j=1;j<=n;j++)
			dis[i][j]=maxl;
		dis[i][i]=0;in[i]=false;
	}
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d",&len[i]);
		for(int j=1;j<=len[i];j++)
			scanf("%d",&a[i][j]);
		if(len[i]==2)
			in[a[i][1]]=1,in[a[i][2]]=1;
		for(int j=1;j<=len[i];j++)
			for(int k=1;k<=len[i];k++)
				dis[a[i][j]][a[i][k]]=min(dis[a[i][j]][a[i][k]],len[i]-1);
	}
} 

inline void init()
{
	for(int i=1;i<=n;i++)
		{
			while(!q[i].empty()) q[i].pop();
			vis[i]=false;
			for(int j=1;j<=n;j++)
			if(j!=i)
				q[i].push(node{dis[i][j],j});
		}
}

inline void mainwork()
{
	bool flag;int mn;node d;
	for(int st=1;st<=n;st++)
	if(in[st])
	{
		init();
		ans[1]=st;flag=true;vis[st]=true;
		for(int j=1;j<=n;j++)
		while(!q[j].empty() && vis[q[j].top().id])
			q[j].pop();
		for(int i=2;i<=n;i++)
		{
			mn=maxl;
			for(int j=1;j<=i-1;j++)
			if(!q[ans[j]].empty())
				mi[j]=q[ans[j]].top();
			for(int j=1;j<=i-1;j++)
			{
				if(q[ans[j]].empty()) continue;
				if(mn>mi[j].dis+j)
				{
					mn=mi[j].dis+j;
					d=mi[j];
				}
			}
			if(mn!=i)
			{
				flag=false;
				break;
			}
			ans[i]=d.id;vis[d.id]=true;
			for(int j=1;j<=n;j++)
			while(!q[j].empty() && vis[q[j].top().id])
				q[j].pop();
		}
		if(flag)
		{
			//puts("ok");
			return;
		}
	}
}

inline void print()
{
	for(int i=1;i<=n;i++)
		printf("%d%c",ans[i],(i==n)?'\n':' ');
}

int main()
{
	int t=1;
	scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值