POJ 2182 Lost Cows(树状数组,暴力解法)

本文详细介绍了如何使用树状数组和暴力解法解决POJ2182 Lost Cows问题。通过实例分析,解释了如何在给定的数据基础上进行推导,最终得到正确的排序结果。提供了两种解决方案:一种是直观的暴力解法,另一种是更高效的树状数组解法,包括AC代码和时间效率比较。

POJ 2182 Lost Cows(树状数组,暴力解法)

http://poj.org/problem?id=2182

题意:

        N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'watering hole' and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands. 
        Regrettably, FJ does not have a way to sort them. Furthermore, he's not very good at observing problems. Instead of writing down each cow's brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow. 
        Given this data, tell FJ the exact ordering of the cows. 

Input

* Line 1: A single integer, N 
* Lines 2..N: These N-1 lines describe the number of cows that precede a given cow in line and have brands smaller than that cow. Of course, no cows precede the first cow in line, so she is not listed. Line 2 of the input describes the number of preceding cows whose brands are smaller than the cow in slot #2; line 3 describes the number of preceding cows whose brands are smaller than the cow in slot #3; and so on. 

Output

* Lines 1..N: Each of the N lines of output tells the brand of a cow in line. Line #1 of the output tells the brand of the first cow in line; line 2 tells the brand of the second cow; and so on.

Sample Input

5
1
2
1
0

Sample Output

2
4
5
3
1

分析:

       其实这道题目只要会手算用例就能暴力解决。

       假设读入题目给的数组a[n],其中a[1]=0

       这道题目只给出了在i之前且比位于第i个位置的值小的值有多少个,我们在纸上分析用例可知,最后一个数的值肯定是a[n]+1.

       然后我们从后往前推,且初始化一个数组vis[n]为全1。vis[i]==1表示当前值为i的数还没出现。首先我们推出最后一个数的值为a[n]+1,所以我们标记vis[a[n]+1]=0,接着我们推n-1的值,假设a[n-1]=3,那么就是在n-1的数之前有3个还未出现的数比它小,那么我们从vis[1]开始扫描,找到第4个1的位置就是a[n-1]的值。然后把这第4个1的位置vis[x]标记0.接下去找n-2位置的值,以此类推即可。

AC代码:266ms

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN =10000;
int a[MAXN];//初始
int vis[MAXN];//标记1
int ans[MAXN];//最终计算的结果
int main()
{
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        a[1]=0;
        for(int i=2;i<=n;i++)
            scanf("%d",&a[i]);

        for(int i=1;i<=n;i++)
            vis[i]=1;
        ans[n]=a[n]+1;
        vis[ans[n]]=0;
        for(int i=n-1;i>=1;i--)
        {
            int num = a[i];
            int j;
            for(j=1;i<=n;j++)
            {
                if(vis[j]==1)
                {
                    num--;
                    if(num<0)
                    {
                        vis[j]=0;
                        break;
                    }
                }
            }
            ans[i]=j;
        }
        for(int i=1;i<=n;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}</span>

 

解法二:树状数组。初始时,树状数组为全0.sum(x)表示已经确定了值的且小于等于x的值的个数。

我们依然从后往前倒推,对于当前考虑的a[n],假设它的值为x,那么我们就执行add(x,1)。接下来我们要找到a[n-1]的值y,y肯定在[1,8000]范围内,所以可以用二分查找确定y的值。如果我们猜y=8的话,如果a[n-1]=4并且比8小的已经确定了的值即sum(7)=3,可以说明从1到7中:由有3个数的值已经确定了 并且 还有4个值没有确定且这些没确定值的比y小的数正好是4个,那么y肯定>=8。可以加大y的范围继续探测。如果sum(7)+4<7的话,8就比y本来的值要大了。

如果sum(7)+4>7的话,8就比y本来的值要小。

       由此可得一般结论,我们猜测当前a[i]的值为y,如果

Sum(y-1)+a[i]==y-1 那么y为最小值

Sum(y-1)+a[i]<y-1 那么y-1为最大值

Sum(y-1)+a[i]>y-1 那么y+1为最小值

 

AC代码:0ms 注意if else语句的顺序,发生概率大的语句放前面。

 

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN =8008;
int a[MAXN];//初始
int ans[MAXN];//最终计算的结果
int c[MAXN];
int n;
int lowbit(int x)
{
    return x&(-x);
}
int sum(int x)
{
    int res=0;
    while(x)
    {
        res +=c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v)
{
    while(x<=n)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}

int main()
{

    while(scanf("%d",&n)==1&&n)
    {
        a[1]=0;
        memset(c,0,sizeof(c));
        for(int i=2;i<=n;i++)
            scanf("%d",&a[i]);

        ans[n]=a[n]+1;
        add(a[n]+1,1);
        for(int i=n-1;i>=1;i--)
        {
            int L=1,R=n;
            while(R>L)
            {
                int mid = (R+L+1)/2;
                if(sum(mid-1)+a[i] < mid-1)//这3个if else语句的顺序换一换 时间就可能是16ms 63ms 0ms等
                    R=mid-1;
                else if(sum(mid-1)+a[i] == mid-1)
                    L=mid;
                else
                    L=mid+1;
            }
            ans[i]=L;
            add(ans[i],1);
        }
        for(int i=1;i<=n;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}
</span>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值