JZOJsenior3967.Counting Friends

本文介绍了一种解决农夫John在管理奶牛社交网络“哞不可”时遇到的问题的方法。该问题涉及从一个包含N+1个数字的列表中找到可能的错误数字,以便列表能正确反映N头奶牛之间的朋友关系。文章详细阐述了如何通过贪心算法确定哪些数字可能是错误添加的,并提供了一个具体的实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

problem

Description

FJ 的N 头奶牛(2<= N<= 500)都加入了社交网络“哞不可” 。
每头奶牛有一个或多个与它们自己在哞不可上互相关注的朋友。为了好玩,FJ 制作了一个列表,记下每头奶牛的朋友数目。但是,在书写列表的过程中,农夫John 惆怅了,以至于他错误地写下了一个额外的数字(因此他的列表包含N + 1 个数字,非他预计的N 个数字)。
请帮助FJ 找出在他的列表中有哪些数字可以是那个错误的额外数字。

Input

第一行:整数N。
第2 至N + 2 行:第i + 1 行包含FJ 的某一头奶牛的朋友数量,或者也许是那个错误的额外数字。

Output

第一行:一个整数K 给出在FJ 的列表中有多少项目可能是那个额外的数字(或者,K = 0 意味着没有一个数字,移除后可以产生一个可行的朋友配对)。
第2 至K + 1 行:每行包含可能是额外的数字按照输入顺序产生的序号(1到N +1)——也就是说,一个移除后,剩下的N 个数字符合奶牛们某一个可行
的朋友关系集合。这K 行的序号须按从小到大排列。

Sample Input

4
1
2
2
1
3

Sample Output

3
1
4
5
样例解释:
FJ 有4 头奶牛。两头每头只有一个朋友,两头各有两个朋友,还有一头有三个朋友(当然,这些数字其中一个是额外的并且不属于这个列表)。
移除FJ 列表的第一个数字(数字1)之后会得到一个剩下的数字2,2,1,3 组成的列表,并且符合一个可行的朋友关系配对——例如,如果我们将奶牛从A
到D 命名,那么配对(A;B); (A;C); (A;D) 以及(B;C) 就足够了,这是因为A有三个朋友,B 和C 都各有两个朋友,还有D 有一个朋友。同理,从FJ 的列表里移除另外一个“1”也是可行的,还有移除“3”也可以。从FJ 的列表里移除任意一个“2”都不可——我们可以看到剩下数字的和是奇数,明显我们无法凑出一个对应的配对。

Data Constraint

对于9% 的数据,N <=10。
对于27% 的数据,N <=100。


analysis

  • 正解贪心

  • 首先O(n)O(n)循环枚举删去哪头牛

  • 然后把剩下nn头牛塞进[1,500]的桶里,O(n2)O(n2)判断是否可行

  • 怎么才可行呢?就是当前最大的数>=>=比它小的数的个数

  • 这个O(n2)O(n2)判断,绝对不能用快排会T的

  • 时间复杂度O(n3)O(n3),数据太水——


code

#include<stdio.h>
#include<string.h>
#define MAXN 505

using namespace std;

int a[MAXN],b[MAXN],f[MAXN],ans[MAXN];
int n,tot;

int min(int x,int y)
{
    if (x<y) return x;
    return y;
}

int main()
{
    //freopen("readin.txt","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<=n+1;i++)
    {
        scanf("%d",&a[i]);
    }
    for (int i=1;i<=n+1;i++)
    {
        memset(b,0,sizeof(b));
        for (int j=1;j<=n+1;j++)
        {
            if (i!=j)b[a[j]]++;
        }
        int flag=1,now=n;
        while (now>0)
        {
            while (!b[now] && now>0)now--;
            if (now<=0)break;
            b[now]--;
            int sum=0,i=now;
            f[0]=0;
            while (sum<now && i>0)
            {
                for (int j=1;j<=min(b[i],now-sum);j++)
                {
                    f[++f[0]]=i;
                }
                sum+=b[i];
                i--;
            }
            if (sum<now) 
            {
                flag=0;
                break;
            }
            for (int j=1;j<=f[0];j++) 
            {
                b[f[j]-1]++;
                b[f[j]]--;
            }
        }
        if (flag)ans[++tot]=i;
    }
    printf("%d\n",tot);
    for (int i=1;i<=tot;i++) 
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值