题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5493
题目大意:
有 N 个人排队,每个人都忘记自己的位置。但是每个人都知道自己的身高 Hi 和自己前
边或后边有 Ki 个比自己高的人。给你每个人的 Hi,Ki,求按身高满足最小序的情况。
如果不满足最小序的情况,则输出"impossible"。
解题思路:
题目要求输出身高字典序最小的情况,那么先对身高按从小到大排序。然后按身高从小
到大确定每个人的位置。由于每次都是从小到大,则每次放进的人都是比之前的人高的。
设当前放进的人是身高从小到大第 i 个人,如果要满足他前边或后边刚好有 k 个人比他
高,则:
如果前边有 k 个人比他高,则现在前边要空出 k 个位置来放置比他高的人,他就放置在
第 k+1 的位置上。
如果后边有 k 个人比他高,已知已经放置了 i-1 个人,除了他自己,还剩下 N - (i-1) - 1
个人。而后边还要放 k 个比他高的人,则前边还应该要空出 N - (i-1) - 1 - k 个位置,则
他自己放置在第 N - i - k + 1 的位置上。
为了使身高的字典序最小,则每次都应该尽可能的靠前。所以应该选择这两种情况中的最
小情况,即 min(k,N - i - k)。
现在考虑不可能的情况。第一种在前边要空出 k 个比他高的位置,已经算上自己共有 i+k
个人,如果这个数应该小于等于 N 否则不能放置( i + k > N,则不能放置)。第二种在前边
空出 N - i - k 个位置,则 N - i - k > 0(同样是i + k > N,则不能放置)。
综上可得:如果对于当前要放的第 i个人,如果 i + k > N,则无解,输出"impossible"。
AC代码:
#include
#include
#include
#include
using namespace std;
const int MAXN = 100010;
const int INF = 0x3f3f3f3f;
int Ans[MAXN];
int Sum[MAXN << 2];
struct Node
{
int h;
int k;
}A[MAXN];
bool cmp(Node a,Node b)
{
if(a.h != b.h)
return a.h < b.h;
return a.k < b.k;
}
void Build(int root,int L,int R)
{
Sum[root] = R - L + 1;
if(L == R)
return;
int Mid = (L + R) >> 1;
Build(root << 1,L,Mid);
Build(root << 1|1,Mid+1,R);
}
void Update(int root,int L,int R,int k,int v)
{
Sum[root]--;
if(L == R)
{
Ans[L] = v;
return;
}
int Mid = (L + R) >> 1;
if(Sum[root << 1] > k)
Update(root<<1,L,Mid,k,v);
else
Update(root<<1|1,Mid+1,R,k-Sum[root<<1],v);
}
int main()
{
int T,N,kase = 0;
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(int i = 1; i <= N; ++i)
scanf("%d%d",&A[i].h,&A[i].k);
sort(A+1,A+1+N,cmp);
Build(1,1,N);
bool flag = true;
for(int i = 1; i <= N; ++i)
{
int k = A[i].k;
int v = A[i].h;
if(k+i > N) //不满足放置的情况
{
flag = false;
break;
}
k = min(k,N-i-k);
Update(1,1,N,k,v);
}
printf("Case #%d:",++kase);
if( !flag )
printf(" impossible\n");
else
{
for(int i = 1; i <= N; ++i)
printf(" %d",Ans[i]);
printf("\n");
}
}
return 0;
}