您的位置:首页 > 其它

NOIP2007 提高组 复赛 game 矩阵取数游戏

2017-06-06 16:43 309 查看
NOIP2007 提高组 复赛 game 矩阵取数游戏

1.审题,弄懂样例。

2 3
1 2 3
3 4 2

取数如下:第1次1 2;第2次2 3;第3次3 4。

求和如下:1*2+2*2+2*4+3*4+3*8+4*8=82

感觉是贪心算法,但又感觉,贪心算法很可能只能得40分。存在反例,中间的数特别小,靠近行首行尾的数特别大。

2.动态规划,关键是找状态,以及状态转移方程,试试。

3.翻看他人做法,https://wenku.baidu.com/view/9db8950190c69ec3d5bb750c.html该篇写得不错,摘抄如下:

分析:此题为区间动归,做了田忌赛马后,此题的动态转移一点也不难,

f【i,j】表示区间(i,j)所能取得的最大值,取数时必取i或j

所以,转移方程式为f【i,j】=max(f【i+1,j】+a【i】,f【i,j-1】+a【j】)*2

边界为f【i,i】=a【i】*2

此题要用高精度,可以只写加法,把乘以二看做两个相加。还有一个高精度比较大小,为了不超时可把进制设为10000或更大。

注意输出的时候要进行补位。

4.http://blog.csdn.net/kingzone_2008/article/details/12589993该篇代码长度比较短,适合研究。

5.
http://www.cnblogs.com/Neptune-/p/5546711.html对上文有进一步的解释:
需要注意的地方:

  1.M和N给的范围略坑啊,2^80只有悄悄不说话了,所以直接上了100000的压位,感觉比高精好使多了

  2.2的次方多次用到,所以预先处理出来待用
6.//NOIP2007 提高组 复赛 game 矩阵取数游戏

//该题搁置有些久了,今天重新拿起进行处理2017-8-23

//同以往,弄明白样例,还是比较重要的。

//样例1,解析如下:

//第一行 1*2^1+2*2^2+3*2^3=34

//第二行 2*2^1+3*2^2+4*2^3=48

//最大得分 34+48=82

//由上述样例可得思路,每次取出该行,行首或行尾的最小值。

//上述思路,属贪心算法,估计只能过30%的数据,试试看。提交,测试点1,3AC 测试点2,4-10WA

//理解贪心算法为什么错,关键是找一个反例。

//搜索网络,发现一个比较好的贪心的反例http://blog.sina.com.cn/s/blog_5ef211b10100cr25.html

//样例2

//输入:

//1 4

//4 5 0 5

//输出:

//122

//样例2分析如下:

 


//样例2动态规划,手动模拟如下:

f[1][4]=max(f[2][4]+a[1],f[1][3]+a[4])*2;

f[2][4]=max(f[3][4]+a[2],f[2][3]+a[4])*2;

f[1][3]=max(f[2][3]+a[1],f[1][2]+a[3])*2;

f[3][4]=max(f[4][4]+a[3],f[3][3]+a[4])*2;

f[2][3]=max(f[3][3]+a[2],f[2][2]+a[3])*2;

f[1][2]=max(f[2][2]+a[1],f[1][1]+a[2])*2;

f[1][1]=a[1]*2;

f[2][2]=a[2]*2;

f[3][3]=a[3]*2;

f[4][4]=a[4]*2;

计算如下:

f[1][1]=a[1]*2; f[1][1]=8
f[2][2]=a[2]*2; f[2][2]=10
f[3][3]=a[3]*2; f[3][3]=0
f[4][4]=a[4]*2; f[4][4]=10

f[1][2]=max(f[2][2]+a[1],f[1][1]+a[2])*2; f[1][2]=max(14,13)*2=28

f[2][3]=max(f[3][3]+a[2],f[2][2]+a[3])*2; f[2][3]=max(5,10)*2=20

f[3][4]=max(f[4][4]+a[3],f[3][3]+a[4])*2; f[3][4]=max(10,5)*2=20

f[1][3]=max(f[2][3]+a[1],f[1][2]+a[3])*2; f[1][3]=max(24,28)*2=56

f[2][4]=max(f[3][4]+a[2],f[2][3]+a[4])*2; f[2][4]=max(25,25)*2=50

f[1][4]=max(f[2][4]+a[1],f[1][3]+a[4])*2; f[1][4]=max(50,56)*2=122
至此,应该可以说,掌握了区间动态规划。

//接下来,采用非高精度算法,进行动态规划,估计能拿60分。

//高精度算法,动态规划,提交,测试点2-10 WA 采用之前编好的非高精度算法,与当前程序,进行数据对比

//查了整整两天,程序的最大问题找到,memset(x,0,30*sizeof(int));//13 该函数的问题查了整整两天,在函数内传入的是x的收地址,x元素的个数在该函数体内未知,故函数体内memset要慎用memset(x,0,sizeof(x)); 4 漏了该句 初始化 

//提交AC,2017-8-26 终于AC,虽然代码写得很乱,但正是宣告,贪心,动态规划,高精度算法,上了一个新台阶,有独立完成的能力。 

总结:考试时,提交程序时,为了保证得分,非高精度算法更好,程序简单,不容易错,通常都能得到60分以上。

附上动态规划,高精度算法,AC代码。

#include <stdio.h>

#include <string.h>

int a[85][85];

int f[85][85][30],z1[30],z2[30],ans[30];//4 改进写法 z1[85][85][30],z2[85][85][30]

void add1(int *x,int y,int *z){//pass //x+y=z 每个元素表示4位,x[][][0]表示数据长度 

    int i; 

    memset(x,0,30*sizeof(int));//13 该函数的问题查了整整两天,在函数内传入的是x的收地址,x元素的个数在该函数体内为止,故函数体内memset要慎用memset(x,0,sizeof(x)); 4 漏了该句 初始化

    for(i=z[0];i>=0;i--)//12漏了两句 

        x[i]=z[i]; 

    x[1]+=y;//10 x[i]=z[i]+y;此句原来写在for循环内部 

    for(i=1;i<=z[0];i++){

        x[i+1]+=x[i]/10000;//9 此处写成 x[i+1]+=z[i]/10000; 查了好久好久 

        x[i]%=10000;

    }

    if(x[i])//5 此处写成 if(z[s][e][i]) 程序改了后,需要修改的地方很多,牵一发动全身 

        x[0]=z[0]+1;//2 低级错误,查了有点久,z[s][e][0]=x[s][e][0]++;x[s][e][0]先赋值,再自加

}

void add2(int *x,int *y){

    int i,len;

    x[0]=x[0]>y[0]?x[0]:y[0];//6  此处写成 ans[0]=ans[0]>y[s][e][0]?ans[0]:y[s][e][0];

    len=x[0]>y[0]?y[0]:x[0];//取小的

    for(i=1;i<=len;i++)//12漏了两句 

        x[i]+=y[i]; 

    for(i=1;i<=x[0];i++){

        x[i+1]+=x[i]/10000;//6 此处写成  ans[i+1]+=ans[i]/10000;

        x[i]%=10000;//6 此处写成  ans[i]%=10000;

    }

    if(x[i])//6 此处写成  if(ans[i])

        x[0]=x[0]+1;//6 此处写成 ans[0]=y[s][e][0]+1;2 低级错误,查了有点久,ans[0]=y[s][e][0]++;y[s][e][0]先赋值,再自加     

}

void mul(int *x,int y,int *z){//x+y=z 每个元素表示4位,x[][][0]表示数据长度 

    int i; 

    z[0]=x[0];//1 漏了该句

    for(i=1;i<=x[0];i++)//11 漏了两句,对乘法运算理解不深刻 

        z[i]=x[i]*y; 

    for(i=1;i<=x[0];i++){

        z[i+1]+=z[i]/10000;

        z[i]%=10000;

    }

    if(z[i])

        z[0]=x[0]+1;//2 低级错误,查了有点久,z[s2][e2][0]=x[s1][e1][0]++; x[s1][e1][0]先赋值,再自加     

}

int cmp(int *x,int *y){//x>y 1 x<y -1 x=y 0

    int i;

    if(x[0]>y[0])return 1;

    else if(x[0]<y[0])return -1;

    else{

        for(i=x[0];i>=1;i--)

            if(x[i]>y[i])

                return 1;

            else if(x[i]<y[i])

                return -1;

        return 0;

    }

}

int main(){

    int i,j,n,m,s,e,k,p;

    memset(f,0,sizeof(f));

    memset(ans,0,sizeof(ans));

    scanf("%d%d",&n,&m);

    for(i=1;i<=n;i++)

        for(j=1;j<=m;j++)//1 此处写成 for(j=1;j<=n;j++)

            scanf("%d",&a[i][j]);

    for(i=1;i<=n;i++){

        memset(f,0,sizeof(f));//8 漏了初始化 

        for(k=1;k<=m;k++)

            f[k][k][0]=1,f[k][k][1]=a[i][k]*2;

        for(k=1;k<=m;k++)

            for(j=1;j<=m;j++)

                if(j+k<=m){

                    add1(z1,a[i][j],f[j+1][j+k]);

                    add1(z2,a[i][j+k],f[j][j+k-1]);

                    if(cmp(z1,z2)==1)

                        mul(z1,2,f[j][j+k]);

                    else

                        mul(z2,2,f[j][j+k]);

                }else

                    break;

        add2(ans,f[1][m]);

    }

    printf("%d",ans[ans[0]]);

    for(i=ans[0]-1;i>=1;i--)

        printf("%04d",ans[i]);//3  printf("%05d",ans[i]); 低级错误 10000 四位即可进位 

    return 0;

}   

//非高精度,动态规划 得分60分 测试点7-10WA 如果是考试,这个得分,应该比较满意了。

#include <stdio.h>

int a[85][85];

long long f[85][85];

long long max(long long a,long long b){

    return a>b?a:b;

}

int main(){

    int i,j,n,m,s,e,k;

    long long ans=0;

    scanf("%d%d",&n,&m);

    for(i=1;i<=n;i++)

        for(j=1;j<=m;j++)//1 此处写成 for(j=1;j<=n;j++)

            scanf("%d",&a[i][j]);

    for(i=1;i<=n;i++){

        for(k=1;k<=m;k++)

            f[k][k]=a[i][k]*2;

        for(k=1;k<=m;k++)

            for(j=1;j<=m;j++)

                if(j+k<=m){

                    f[j][j+k]=max(f[j+1][j+k]+a[i][j],f[j][j+k-1]+a[i][j+k])*2;

                }else

                    break;

        ans+=f[1][m];

    }

    printf("%lld\n",ans);

    return 0;

}   

//附上贪心算法,得分20 

#include <stdio.h>

int a[85][85];

long long mul(int k){

    long long ret=1,i;

    for(i=1;i<=k;i++)

        ret*=2;

    return ret;    

}

int main(){

    int i,j,n,m,s,e,k;

    long long ans=0;

    scanf("%d%d",&n,&m);

    for(i=1;i<=n;i++)

        for(j=1;j<=m;j++)//1 此处写成 for(j=1;j<=n;j++)

            scanf("%d",&a[i][j]);

    for(i=1;i<=n;i++){

        s=1,e=m,k=0;

        while(s<=e){

            k++;

            if(a[i][s]<a[i][e]){

                ans+=a[i][s]*mul(k);

                s++;

            }

            else{

                ans+=a[i][e]*mul(k);

                e--;

            }

        }

    }

    printf("%lld\n",ans);

    return 0;

}   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: