cf534D 枚举握手次数

题意:
      有n个学生进教室,先后顺序不同,每个人进去后会和当前在教室里的人握手,并且记录人数,而且当教室里有超过三个人的时候 他们有可能组队去参加比赛,后来的人看不到他们。


思路:

     这个题目还行挺有意思的,我们可以一个人一个人来模拟,就是枚举握手次数,然后在相应的里面找到一个,如果一个都找不到就-3,到最后就行了,比如一开始我们枚举0,就是握手次数是0的,如果找到有0,那么这个人就是第一个人,如果0的不唯一的话随便挑一个就行,找到后就+1,找握手次数为1的,找到后就是第二个进来的,然后+1,找2的...如果当前握手次数找不到的话就-3,找到就继续,还找不到就还-3,这样如果那次当前枚举次数小于0了,那么就说明失败了,就是无解,还有就是找到的时候不能暴力去找,可以用个二维容器什么的,我用的是前向星配合类似DINIC里面那个优化(职业病啊),比如当前i这个人的握手次数是3,那么就连边3->i,这样当我们想在3里面删除的时候随便找一个就行,删除之后记得这样处理 list[s] = E[k].next,就是下次直接从下一个开始就行,这个地方怎么处理都行,顺手就行,用容器还有链表啥的都行,不多说了,关键是想到枚举握手次数的那个地方就好办了。


#include<stdio.h>
#include<string.h>

#define N_node 200005
#define N_edge 250005

typedef struct
{
    int to ,next;
}STAR;

STAR E[N_edge];
int list[N_node] ,tot;
int mkc[N_node];
int Ans[N_node] ,AT;

void add(int a ,int b)
{
    //printf("%d %d**
" ,a ,b);
    E[++tot].to = b;
    E[tot].next = list[a];
    list[a] = tot;
}

int main ()
{
    int n ,i ,a ,b;
    while(~scanf("%d" ,&n))
    {
        memset(list ,0 ,sizeof(list)) ,tot = 1;
        memset(mkc ,0 ,sizeof(mkc));
        for(i = 1 ;i <= n ;i ++)
        {
            b = i;
            scanf("%d" ,&a);
            add(a ,b);
            mkc[a] ++;
        }
        int nowc = 0;
        AT = 0;

        while(nowc >= 0 && AT != n)
        {
            if(!mkc[nowc]) nowc -= 3;
            else
            for(int k = list[nowc] ;k ;k = E[k].next)
            {
                 Ans[++AT] = E[k].to;
                 mkc[nowc]--;
                 list[nowc] = E[k].next;
                 nowc ++;
                 break;
            }
            //printf("%d
" ,nowc);
        }
        if(AT == n)
        {
            printf("Possible
");
            for(i = 1 ;i <= n ;i ++)
            if(i == n) printf("%d
" ,Ans[i]);
            else printf("%d " ,Ans[i]);
        }
        else printf("Impossible
");
    }
    return 0;

}







原文地址:https://www.cnblogs.com/csnd/p/12062411.html