NOIP2007 提高组 复赛 game 矩阵取数游戏
2017-06-06 16:43
309 查看
NOIP2007 提高组 复赛 game 矩阵取数游戏
1.审题,弄懂样例。
取数如下:第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;
}
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;
}
相关文章推荐
- NOIP2007提高组——矩阵取数游戏(game)
- dp+高精度(洛谷1005 矩阵取数游戏NOIP 2007 提高第三题)
- [NOIP2007] 提高组 洛谷P1005 矩阵取数游戏
- 【NOIP2007提高组T3】矩阵取数游戏-动态规划+高精度
- NOIP2012 提高组 复赛 day1 game 国王游戏 再见
- [NOIp2007提高组]矩阵取数游戏
- [NOIP2007] 矩阵取数游戏
- 【NOIP2007】洛谷1005 矩阵取数游戏
- NOIP2011复赛提高组day1(A:铺地毯 B:选择客栈 C:mayan游戏)
- [NOIP2007] 矩阵取数游戏 【记忆化搜索+高精】
- 洛谷 P1005 Tyvj P1054 [NOIP2007 T3] 矩阵取数游戏 [60分 非高精]
- 洛谷P1005&NOIP2007 矩阵取数游戏
- NOIP2007【矩阵取数游戏】
- NOIP2007 矩阵取数游戏 [dp] [高精度]
- NOIP 提高组复赛 day1 国王游戏
- NOIP2007提高组 矩阵取数
- NOIP2013复赛提高组day1(A:转圈游戏 B:火柴排队 C:货车运输)
- [NOIP 2007] 矩阵取数游戏:DP,高精度
- noip2007 矩阵取数游戏 (动态规划+高精度)
- ACM 96. [NOIP2007] 矩阵取数游戏(dp+高精度)