• 2018.02.12 noip模拟赛T2


    二兵的赌注

    Description
    游戏中,二兵要进入了一家奇怪的赌场。
    赌场中有n个庄家,每个庄家都可以猜大猜小,猜一次一元钱。
    每一次开彩前,你都可以到任意个庄家那里下赌注。
    如果开彩结果是大,你就可以得到你之前猜大的庄家相应的ai元钱。
    如果开彩结果是小,你就可以得到你之前猜小的庄家相应的bi元钱。
    你可以在同一个庄家那里既猜大又猜小,(这样是两块钱),也可以什么都不猜(这样不用钱)。
    问怎么样下注,才能赢得最多的有保障的钱。
    有保障的钱指不管开彩结果是大是小,你都能够赢得相应的钱。
    你能帮助他计算这个值吗?
    Input
    第一行一个数字N。表示有N个庄家。
    接下来N行,每行2个实数,分别表示这个庄家的ai和bi。
    Output
    一个四位小数,表示最多能赢的有保障的钱。
    Sample Input
    4
    1.4 3.7
    1.2 2
    1.6 1.4
    1.9 1.5
    Sample Output
    0.5000
    HINT
    样例中,最好的策略是赌第一个庄家开小,第三、第四个庄家开大
    测试点 N
    1 N≤10


    2
    3 N≤1000


    4
    5
    6
    7 N≤100000
    8
    9
    10


    1.0 ≤ ai, bi ≤ 1000.0

    1s/128M

     

    好了,拿到题先花一定的时间弄清题意。

    然后我们就会发现:押大小和哪个庄家之间完全没有关系,一个庄家的大和小完全是独立的。继续分析,显然押越大的越好。

    于是我们分别sort一下。

    然后我开始了分析:首先读入时-=1,然后,设大或小为a[]和b[],分别取前i.j个押。

    然后对于每对确定的i,j,易知s=min(suma[i]-j,sumb[j]-i)其中sum为前缀和。

    我们设suma[i]-j=左,sumb[j]-i=右

    然后定性分析:

    当j固定时,i+则左+右-,i-则左-右+

    当i固定时,j+则左-右+,j-则左+右-

    再次分析可知对于一对(i,j),若i+,则可能存在的ans>s必须令j+

    于是有了以下思路:for(i:1...n) 对于每个i找到匹配的j,则i++时j不用从头开始枚举,只需继续j++,于是时间复杂度O(n)完成(不记sort)

    以上是我在考场上的思路。不管思考过程多么艰难以至于花费1hour才想出来,但是结果就是,和标准程序简直一模一样。但是代码实现上就很naive了。

    同一个思路,我代码实现起来写了都要40+行,再一看标程,不到十行就解决了,尴尬......

    我的代码大概是这样

     1 #include <cstdio>
     2 using namespace std;
     3 int a[10],b;
     4 void c();
     5 int main()
     6 {
     7 
     8     ///读入
     9     ///sort一波
    10     /**
    11     for(i:1...n)
    12     {
    13         ax=xx
    14         bx=xx
    15         c=xx
    16         while(j<=n && j<=c+a[i])
    17         {
    18             if(xXX)
    19                 xxx
    20             else if(xxx)
    21                 xxx
    22             else
    23             xxx
    24         }
    25         d=xx
    26         e=xx
    27         f=xx
    28     }
    29     */
    30     return 0;
    31 }
    my code

    瞎设变量,大概近十个。

    然后结果就是:推了一小时,打了半小时,最后又调了半小时。结果还是错的跟翔一样。

    还有一个很奇怪的事:scanf读入double会出现蜜汁读入0,int就好得很。只好用cin缓慢读入了。找时间问一下雨菲,她肯定又不会理我。。。

    那么来看看标程:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <assert.h>
     5 #include <iostream>
     6 #include <sstream>
     7 #include <vector>
     8 #include <string>
     9 #include <math.h>
    10 #include <queue>
    11 #include <list>
    12 #include <algorithm>
    13 #include <map>
    14 #include <set>
    15 #include <stack>
    16 #include <ctime>
    17 using namespace std;
    18 
    19 #define ALL(c) (c).begin(),(c).end()
    20 #define IN(x,c) (find(c.begin(),c.end(),x) != (c).end())
    21 #define REP(i,n) for (int i=0;i<(int)(n);i++)
    22 #define FOR(i,a,b) for (int i=(a);i<=(b);i++)
    23 #define INIT(a,v) memset(a,v,sizeof(a))
    24 #define SORT_UNIQUE(c) (sort(c.begin(),c.end()), c.resize(distance(c.begin(),unique(c.begin(),c.end()))))
    25 template<class A, class B> A cvt(B x) { stringstream ss; ss<<x; A y; ss>>y; return y; }
    26 
    27 typedef pair<int,int> PII;
    28 typedef long long int64;
    29 
    30 int n=0;
    31 double a[100000],b[100000];
    32 
    33 int main() {
    34     freopen("coin.in","r",stdin);
    35     freopen("coin.out","w",stdout);
    36     cin >> n;
    37     REP (i,n) {
    38         cin >> a[i] >> b[i];
    39         a[i]-=1; b[i]-=1;
    40     }
    41     sort(a,a+n); reverse(a,a+n);
    42     sort(b,b+n); reverse(b,b+n);
    43     int i=0,j=0;
    44     double r=0;
    45     double sa=0,sb=0;
    46     while (i<n) {
    47         sa+=a[i];
    48         i++;
    49         while (j<n && min(sa-j,sb-i)<min(sa-(j+1),(sb+b[j])-i)) {
    50             sb+=b[j];
    51             j++;
    52         }
    53         r=max(r, min(sa-j,sb-i));
    54     }
    55     printf("%.4f\n",r);
    56     return 0;
    57 }
    标程

    那么我们忽略上面一长串的头文件和宏定义。仔细观察关键部分可以发现:我那冗杂的程序和变量得到了极大的简化,强!喵!喵不可言啊!

    接下来是文字答案中的二分,利用了单调性。

    给出来:

    Coin
    算法零:
    输出0即可。
    算法一:
    直接O(4^n)暴力枚举,期望得分20分。
    算法二:
    贪心,问题所求的是最大化min(∑ai ?na ?nb,∑bj ?na ?nb),na, nb分别表示猜大猜小的个数。将ai, bi全部减一,所求变为min(∑ai ?nb,∑bj ?na)。我们枚举na, nb,贪心取最大的na个ai和nb个ai即可。
    复杂度O(n^2),期望得分60分
    算法三:
    贪心+二分,假设na固定,那么ai的选取也是确定的。假设nb从0开始一个个往上增加,∑bj –na递增,∑ai –nb递减。min(∑ai ?nb,∑bj ?na) 在∑bj –na超过∑ai –nb前递增。所以只需二分∑bj –na什么时候超过∑ai –nb即可。
    复杂度O(n log n),期望得分100分。
    标程:
    直接sort+O(n)
    思路跟我的一样可惜我TM写了30-40行的东西他十行不到搞定。
    仍需努力呀。

    我自己打了测了一下,最大的一个数据正确,但是跑了1.2s还是1.4s(算上了编译时间)

    我的代码:

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <algorithm>
     4 using namespace std;
     5 const int N=100004;
     6 int n;
     7 double ans=-1;
     8 double a[N],b[N];
     9 double suma[N],sumb[N];
    10 bool cmp(double a,double b){return a>=b;}
    11 int tw_divide(int l,int r,int i)
    12 {
    13     if(l==r) return r;
    14     int mid=(l+r)>>1;
    15     double now=min(suma[i]-mid,sumb[mid]-i);
    16     double nowl=min(suma[i]-mid+1,sumb[mid-1]-i);
    17     double nowr=min(suma[i]-mid-1,sumb[mid+1]-i);
    18     if(now>=nowl && now>=nowr) return mid;
    19     if(now<nowl) return tw_divide(l,mid-1,i);
    20     if(now<nowr) return tw_divide(mid+1,r,i);
    21 }
    22 int main()
    23 {
    24     freopen("coin.in","r",stdin);
    25     //freopen("coin.out","w",stdout);
    26     scanf ("%d",&n);
    27     for(int i=1;i<=n;i++)
    28     {
    29         cin>>a[i]>>b[i];
    30         a[i]-=1;b[i]-=1;
    31     }
    32     sort(a+1,a+n+1,cmp);
    33     sort(b+1,b+n+1,cmp);
    34 
    35     for(int i=1;i<=n;i++)
    36     {
    37         suma[i]=suma[i-1]+a[i];
    38         sumb[i]=sumb[i-1]+b[i];
    39     }
    40     double now;
    41     int j;
    42     for(int i=1;i<=n;i++)
    43     {
    44         j=tw_divide(1,n,i);
    45         now=min(suma[i]-j,sumb[j]-i);
    46         ans=max(ans,now);
    47     }
    48     printf("%.4f",ans);
    49     return 0;
    50 }
    my code

    我还是比较喜欢第一种。二分时间多个logn而且还难得写。

  • 相关阅读:
    js禁用回退键
    css和js引用图片路径
    js 文字横向滚动
    数组转换
    Vbox共享串口
    office2003 打开docx文件
    注销退出客户点击回退怎么办
    vs2010 安装mvc3
    修改头像
    iis发布网站局域网无法访问
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/8445975.html
Copyright © 2020-2023  润新知