2016 Multi-University Training Contest 7

1. 1002-HDU 5810 Balls and Boxes

题意:给定n个球,m个盒子,将n个球抛入m个盒子,对于每种情况,定义 是第i个盒子中球的个数, 是所有盒子中的球的个数的平均数,即n/m。求V的期望E(V)。

题解:

首先化简公式。


又由于

所以


首先对于每个盒子,每个球落入其中的概率是p=1/m,落不进的概率为1-p=1-1/m。所以对于每个盒子, 满足二项分布。


所以

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        LL fenzi=1ll*n*(m-1),fenmu=1ll*m*m;
        LL g=__gcd(fenzi,fenmu);
        printf("%I64d/%I64d\n",fenzi/g,fenmu/g);
    }
    return 0;
}

2. 1005-HDU 5813 Elegant Construction

题意:让你构造出一个图使得其满足第i个点能到达的点的个数为ai个,没有环和重边。

题解:按 ai 从小到大排序,在比 i 点与比其 ai 值小的点 j 之间连 aj 条边即可,只要出现 ai 值大于i,则一定不可能满足。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX=1000+10;
struct node
{
    int id,x;
    bool operator <(const node &a)
    {
        return x<a.x;
    }
}a[MAX];


int main()
{
    int T;
    scanf("%d",&T);
    for(int tcase=1;tcase<=T;tcase++)
    {
        int n;
        scanf("%d",&n);
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i].x);
            sum+=a[i].x;
            a[i].id=i+1;
        }
        sort(a,a+n);
        int flag=1;
        for(int i=0;i<n;i++)
        {
            if(a[i].x>i)
            {
                flag=0;
                break;
            }
        }
        printf("Case #%d: %s\n",tcase,flag?"Yes":"No");
        if(flag)
        {
            printf("%d\n",sum);
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<a[i].x;j++)
                {
                    printf("%d %d\n",a[i].id,a[j].id);
                }
            }
        }
    }
    return 0;
}

3. 1009-HDU 5818 Joint Stacks

题意:模拟栈操作,并在栈操作基础上多加一种操作merge A B,该操作可将栈A和栈B内的元素按输入顺序塞进栈A中,并清空栈B。问在执行pop操作时,弹出的元素是多少?

题解:(参考:http://blog.csdn.net/queuelovestack/article/details/52164782

关键是merge操作,如果重新再操作一次序列复杂度太高。这个合并起来的 应该一直是保存的,为merge操作准备。我们可以用一个数组来模拟。用两个指针来指向a和b分栈顶元素。用一个pre[]数组来记录每个位置的前驱。

每次push操作,在数组后面添加元素,移动相应指针。

每次merge操作,如merge A B ,将A指针指向a,b指针较高的一个,B指针指向栈底0位置;并将pre[a]=-1,表示该位置前面的数都为一个栈中的值。

每次pop操作,将当前位置标记为已经删除。如果pre值为-1,则一直往前删直到遇到一个没有被删除(执行过弹出操作)的数,否则直接将指针前移即可。

贴上流程(非原创):



代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX=1e5+10;
int s[MAX],del[MAX];
int pre[MAX];
int a,b,cnt;

int main()
{
    //freopen("in.txt","r",stdin);
    int cas=1;
    int n;
    while(scanf("%d",&n)&&n)
    {
        printf("Case #%d:\n",cas++);
        char op[10],ob,oba,obb;
        int num;
        memset(pre,0xff,sizeof(pre));
        memset(del,0,sizeof(del));
        int a=0,b=0,cnt=0;
        for(int i=0;i<n;i++)
        {
            scanf("%s",op);
            if(op[1]=='u')
            {
                scanf(" %c %d",&ob,&num);
                s[++cnt]=num;
                if(ob=='A')
                {
                    pre[cnt]=a;
                    a=cnt;
                }
                else
                {
                    pre[cnt]=b;
                    b=cnt;
                }
            }
            else if(op[1]=='o')
            {
                scanf(" %c",&ob);
                if(ob=='A')
                {
                    printf("%d\n",s[a]);
                    del[a]=1;
                    if(pre[a]==-1)
                    {
                        do
                        {
                            a--;
                        }while(del[a]==1);
                        pre[a]=-1;
                    }
                    else a=pre[a];
                }
                else
                {
                    printf("%d\n",s[b]);
                    del[b]=1;
                    if(pre[b]==-1)
                    {
                        do
                        {
                            b--;
                        }while(del[b]==1);
                        pre[b]=-1;
                    }
                    else b=pre[b];
                }
            }
            else if(op[1]=='e')
            {
                scanf(" %c %c",&oba,&obb);
                if(oba=='A')
                {
                    a=max(a,b);
                    b=0;
                    pre[a]=-1;
                }
                else
                {
                    b=max(a,b);
                    a=0;
                    pre[b]=-1;
                }
            }
        }
    }
    return 0;
}
另外,标程写的简洁到爆啊。

比较简单巧妙的一个做法是引入一个新的栈C,每次合并的时候就把A和B合并到C上,然后把A和B都清空. push还是按正常做,pop注意当遇到要pop的栈为空时,因为题目保证不会对空栈进行pop操作,所以这时应直接改为对C栈进行pop操作. 这样做因为保证每个元素最多只在一次合并中被处理到,pop和push操作当然也是每个元素只做一次,所以总复杂度是O(N)的.

用到一个merge函数:

merge函数的作用是:将两个有序的序列合并为一个有序的序列。

函数参数:merge(first1,last1,first2,last2,result,compare);//firs1t为第一个容器的首迭代器,last1为第一个容器的末迭代器,first2为第二个容器的首迭代器,last2为容器的末迭代器,result为存放结果的容器,comapre为比较函数(可略写,默认为合并为一个升序序列)。


代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX=1e5+10;
int x[MAX];
int sta[3][MAX],top[3];

int main()
{
    int cas=1;
    int n;
    while(scanf("%d",&n)&&n)
    {
        printf("Case #%d:\n",cas++);
        char op[10],s[3];
        memset(top,0,sizeof(top));
        for(int i=0;i<n;i++)
        {
            scanf("%s %s",op,s);
            int ob=s[0]-'A';
            if(op[1]=='u')
            {
                scanf("%d",&x[i]);
                sta[ob][++top[ob]]=i;
            }
            else if(op[1]=='o')
            {
                if(!top[ob]) ob=2;
                printf("%d\n",x[sta[ob][top[ob]--]]);
            }
            else
            {
                scanf("%s",s);
                top[2]=merge(sta[0]+1,sta[0]+top[0]+1,sta[1]+1,sta[1]+top[1]+1,sta[2]+top[2]+1)-sta[2]-1;
                top[0]=top[1]=0;
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值