NOIP2012 提高组 复赛 day1 game 国王游戏 再见

时间:2022-12-16 14:58:50

NOIP2012 提高组 复赛 day1 game 国王游戏 再见

2017-1-15 20:48

1.经过近半年的历练,重回此题,一看样例解释,马上发现这是一个回溯问题,需用到深度优先遍历,效果等同于找出本人水平有所上升,开始编码。
2.看了下数据量,注意剪枝,应该能得更多的分。
3.随着编程的深入,发现还不是上面认识的问题,这里检讨一下。
4.此题不易,搜索网络,发现是贪心+高精度算法,好吧,继续学习。

5.

考试中容易想到的方法,与http://blog.csdn.net/alan_cty/article/details/50889524雷同。

我们设NOIP2012 提高组 复赛 day1 game 国王游戏 再见 。考虑排最后的人,他的得分即为NOIP2012 提高组 复赛 day1 game 国王游戏 再见 ,既然我们需要让他的得分尽量小,那么我们就需要把 a b 最大的那一个人放在最后。然后,对于每一段序列我们都可以执行这个操作。这就相当于把每个人按 a b 从小到大排一遍序!这样问题就得到了完美的解决。

 

http://blog.sina.com.cn/s/blog_b187cbe10101dvey.html启发,下面给出按A*B的递赠数列排序的证明,考场中不可能花这么多时间,此类证明属于事后诸葛亮。

考虑交换相邻两个二元组的影响,交换i和i+1,实际上只影响到Fi和Fi+1

设T=A1*A2*A3*……*Ai-2*Ai-1

方案A:Fi=T/Bi,Fi+1=T*Ai/Bi+1     (i在i+1之前)

方案B:Fi=T/Bi+1,Fi+1=T*Ai+1/Bi     (i+1在i之前)

 

1.若:1/Bi<Ai/Bi+1,1/Bi+1<Ai+1/Bi

(1)若A方案优于B方案,Ai/Bi+1<Ai+1/Bi

要求:Ai*Bi<Ai+1*Bi+1

(2)若B方案优于A方案,Ai+1/Bi<Ai/Bi+1

要求:Ai+1*Bi+1<Ai*Bi

此种情况要求,按A*B的递赠数列排序。

2.若1/Bi<Ai/Bi+1,1/Bi+1>=Ai+1/Bi

(1)若A方案优于B方案, Ai/Bi+1<=1/Bi+1,显然不成立。

(2)若B方案优于A方案,Ai/Bi+1>=1/Bi+1,显然成立。

此种情形,只有B方案优于A方案。

要求:

1/Bi<Ai/Bi+1=>Ai*Bi>Bi+1;

1/Bi+1>=Ai+1/Bi=>Ai+1*Bi+1<=Bi+1

即Ai*Bi>Bi+1>=Ai+1*Bi+1=>AiBi>=Ai+1*Bi+1

此种情况要求,按A*B的递赠数列排序。

3.若1/Bi>=Ai/Bi+1,1/Bi+1<Ai+1/Bi

(1)若A方案优于B方案,1/Bi<=Ai+1/Bi,显然成立。

(2)若B方案优于A方案, 1/Bi>Ai+1/Bi,显然不成立。

此种情形,只有A方案优于B方案。

要求:

1/Bi>=Ai/Bi+1=>Ai*Bi<=Bi+1即Ai*Bi<=Bi+1<=Ai+1*Bi+1即

Bi<=Ai*Bi<=Bi+1<=Ai+1*Bi+1

1/Bi+1<Ai+1/Bi=>Ai+1*Bi+1>Bi即Ai+1*Bi+1>Bi

综上:Bi<=Ai*Bi<=Bi+1<=Ai+1*Bi+1

此种情况要求,按A*B的递赠数列排序。

4.若1/Bi>=Ai/Bi+1,1/Bi+1>=Ai+1/Bi

(1)若A方案优于B方案,1/Bi<=1/Bi+1 即Bi+1<=Bi

要求:1/Bi>=Ai/Bi+1=>Ai*Bi<=Bi+1

即Ai*Bi<=Bi+1<=Ai+1*Bi+1;

1/Bi+1>=Ai+1/Bi=>Ai+1*Bi+1<=Bi;

Ai+1*Bi+1<=Bi<=Ai*Bi

显然矛盾,此种情形不成立。

(2)若B方案优于A方案,1/Bi>=1/Bi+1即Bi+1>=Bi

要求:1/Bi>=Ai/Bi+1=>Ai*Bi<=Bi+1

即Ai*Bi<=Bi+1<=Ai+1*Bi+1;

1/Bi+1>=Ai+1/Bi=>Ai+1*Bi+1<=Bi;

Ai+1*Bi+1<=Bi<=Ai*Bi

显然矛盾,此种情形不成立。

此种情况,没有最优方案。

综合以上四点,最有方案要求按A*B的递赠数列排序。

6.按一般的情形进行编码,不考虑高精度算法,猜测能得60分。

7.提交,得分60,没有采用高精度算法,里面用了long long 尽可能得扩充数据长度,下面的任务是采用高精度算法。

8.想一想高精度算法,结果的长度不确定,乘法:存储数据若是自左往右,要考虑堆栈,若是自右往左,要考虑最大长度;除法觉得相比乘法,简单些。

9.先将当前数据转化为字符数据,可以采用函数(整数转化为字符函数),也可以自个编个函数(采用除、模

方式),

10.是搜索网络,学习高精度算法,还是自个尝试编一个呢,为了提升能力,选择后者。

11.仔细想了想,用字符来计算乘还是简单的,设置一个进位数组即可,但除法感觉遇到麻烦,需要借位,那么采用移动栈的top标识来处理。看还是不看他人写法,为了提升能力,选择后者。

12.准备用字符数组来存储较长整数,写着写着,觉得挺烦,马上果断改整数数组,希望能简洁。

13.高精度算法,乘法编好,没有参考任何资料,独立完成的,如下:

//2012 game 国王游戏 高精度算法 乘multply 除divide
#include <stdio.h>
#include <string.h>
int product[10000];//乘积
int quotient[10000];//商数
int carry[10000];//进位
//高精度算法*
void multply(int *a1,int na1,int *b1,int nb1,int *product,int *np){//na1字符串a1长度,nb1字符串b1长度//np乘积数字长度
    int i,j;
    int a2,b2,p,c;
    memset(product,0,sizeof(product));    
    for(i=0;i<nb1;i++){
        memset(carry,0,sizeof(carry));
        for(j=0;j<na1;j++){
            product[j+i]+=b1[i]*a1[j]+carry[j+i];
            carry[j+i+1]=product[j+i]/10;
            product[j+i]%=10;
        }
        product[j+i]+=carry[j+i];//最高位进位处理        
    }
    if(product[nb1+na1-1]==0)
        *np=nb1+na1-1;
    else
        *np=nb1+na1;
}
void divide(int *a1,int na1,int *b1,int nb1,int *quotient,int *nq){
    
}
int main(){
    int a[]={9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9};
    int b[]={9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9};
    int i;
    int np;
    int nq;
    multply(a,18,b,18,product,&np);
    for(i=np-1;i>=0;i--)
        printf("%d",product[i]);
    return 0;
}
14.高精度算法,除法,感觉还挺难的,无从下手,先想一想,再说。
15.参考了他人代码,才发现编写的高精度算法是一个普适算法,难度过大,可以考虑编写超过long long 的数据除以long long 的数据,那么,难度一下降低了好几个台阶。目标是独立完成。

16.高精度除,很快编好,编着编着才发现曾经编过,核心代码类似《算法竞赛入门经典(第2版)》P35 习题2-5 分数化小数

2017-1-26 8:47

17.经过一番努力,发现问题重重,无奈,寻找好的编码,进行学习。在洛谷里寻找耗时最短,内存最少的代码进行研究,发现许多代码是ifelse语句,也就是直接对答案进行输入输出,很多是骗人的写法,好不容易,找了一个耗时15ms的代码进行学习研究。

18.代码写得确实好,a[0]用来存储位数,a[1]开始每个元素用来存储整数的4位。算法的局限在于除运算,除数不能超过4位数。

2017-1-29 15:05

19.程序是这样的,想想很难,但编着编着,也就这么回事。

附上AC代码,编译环境Dev-C++4.9.9.2

//2012 国王游戏 game9
#include <stdio.h>
#include <string.h>
struct node{
    int left;
    int right;
    int w;//左右手乘积
}m[1000+10],k,mt;//minister,king
int a[1000+10],b[1000+10],ans[1000+10];//product quotient [0]储备位数
void multply(int *a,int x){//高精度*单精度 高精度每个整数数组元素表示单精度4位
    int i,j;
    int t;
    j=0;
    for(i=1;i<=a[0]||j;i++){
        t=a[i]*x+j;
        a[i]=t%10000;//留下的数据
        j=t/10000;//进位
    }
    a[0]=i-1;
}
void divide(int *a,int x){//高精度/单精度 高精度每个整数数组元素表示单精度4位,注意x要控制在4位数,否则,容易越界
    int i,j;
    int t;
    j=0;
    for(i=a[0];i>=1;i--){
        t=a[i]+j*10000;
        b[i]=t/x;
        j=t%x;
    }
    for(i=a[0];i>=1;i--)
        if(b[i]!=0)
            break;
    if(i==0)
        b[0]=1;
    else
        b[0]=i;
}
int cmp(int *b,int *a){//高精度比大小 b>a 返回值大于0,b==a返回值为0
    int i;
    if(b[0]!=a[0])
        return b[0]-a[0];
    for(i=b[0];i>=1;i--)
        if(b[i]!=a[i])
            return b[i]-a[i];
    return 0;
}
int main(){
    int n;
    int i,j;
    scanf("%d",&n);
    scanf("%d%d",&k.left,&k.right);
    for(i=1;i<=n;i++){
        scanf("%d%d",&m[i].left,&m[i].right);
        m[i].w=m[i].left*m[i].right;
    }
    for(i=1;i<=n;i++)//冒泡排序,由小到大排序,因数据量不大,故采用
        for(j=i+1;j<=n;j++)
            if(m[i].w>m[j].w){
                mt=m[i];
                m[i]=m[j];
                m[j]=mt;
            }
    memset(ans,0,sizeof(ans));
    a[0]=1;
    a[1]=k.left;
    ans[0]=1;
    for(i=1;i<=n;i++){
        divide(a,m[i].right);
        if(cmp(b,ans)>0)//此处一直会出问题不是if(cmp(b,ans))而是if(cmp(b,ans)>0),因为com(b,ans)<0 if语句也执行,曾经犯过这样的错  
            memcpy(ans,b,(b[0]+1)*sizeof(int));
        multply(a,m[i].left);
    }
    //打印结果
    printf("%d",ans[ans[0]]);
    for(i=ans[0]-1;i>=1;i--)
        printf("%04d",ans[i]);
    printf("\n");
    return 0;
}


附上60分代码,编译环境Dev-C++4.9.9.2

//2012 game 国王游戏
#include <stdio.h>
struct node{
    int a;
    int b;
    long long product;//乘积 
}minister[1000+10],king,t;
long long gold[1000+10];
int main(){
    int n;
    int i,j;
    int a,b;
    long long ans;
    long long t2;
    scanf("%d",&n);
    scanf("%d%d",&king.a,&king.b);
    for(i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        minister[i].a=a;
        minister[i].b=b;
        minister[i].product=a*b;
    }
    for(i=1;i<=n;i++)//因数据量比较小,采用冒泡排序,按a*b的乘积自小到大排序 
        for(j=i+1;j<=n;j++)
            if(minister[i].product>minister[j].product){
                t=minister[i];
                minister[i]=minister[j];
                minister[j]=t;
            }
    
    for(i=1;i<=n;i++){//计算每个大臣手上金币
        ans=king.a;
        for(j=1;j<=i-1;j++){
            ans*=minister[j].a;
        }
        ans/=minister[i].b;
        gold[i]=ans; 
    }
    
    for(i=1;i<=n;i++)//按金币自大到小排序 
        for(j=i+1;j<=n;j++)
            if(gold[i]<gold[j]){
                t2=gold[i];
                gold[i]=gold[j];
                gold[j]=t2;
            }
    printf("%lld\n",gold[1]); //对%lld输出总有隐忧,因linxu与windows下不同 
    return 0;