• 【NOIP2007】矩阵取数问题


    题目描述:

    帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的(n imes m)的矩阵,矩阵中的每个元素(a(i,j))均为非负整数。
    游戏规则如下:
    1.每次取数时须从每行各取走一个元素,共(n)个。经过(m)次后取完矩阵内所有元素;

    2.每次取走的各个元素只能是该元素所在行的行首或行尾;

    3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值( imes 2^i) ,其中(i)表示第(i)次取数(从(1)开始编号);

    游戏结束总得分为(m)次取数得分之和。

    帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

    思路:

    看上去是一个矩阵问题,但是实际上,我们可以发现,每一行是相互独立的,也就是说,我们不要从“次”上来研究,而是要根据“行”来研究,最后把每一行的结果相加就可以了。

    单独看一行,这个问题就简化成了:有一个序列,只能从两头拿,每次拿的得分为物品值乘上一个权值(和次数有关),要求最终得分最大。

    是不是很熟悉?对了,和这道题异曲同工:https://www.luogu.com.cn/problem/P2858 没做过这道题的同学可以先做一下这个简化版。

    本题是区间DP老套路了,分从头拿和从尾拿可以分两种情况转移:

    (dp[i][j]=max(a[i] * (1<<m-(j-i)) + dp[i+1][j] , a[j] * (1<<m-(j-i)) + dp[i][j-1])), dp[i][j]表示剩下i-j个数时之后能取得的最大值。
    然后可以写出这样的程序:

    #include<cstdlib>
    #include<algorithm>
    #define ull unsigned long long
    using namespace std;
    ull a[81][81],dp[81][81][81];
    int main(){
    	int i,j,n,m,k;
    	ull ans=0;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)
    		for(j=1;j<=m;j++)
    			scanf("%lld",&a[i][j]);
    	for(i=1;i<=n;i++){
    		for(j=1;j<=m;j++){
    			dp[i][j][j]=a[i][j]*(1ll<<m);
    			for(k=j-1;k;k--){
    				dp[i][k][j]=max(a[i][k]*(1ll<<m-(j-k))+dp[i][k+1][j],a[i][j]*(1ll<<m-(j-k))+dp[i][k][j-1]);
    			}
    		}
    	}
    	for(i=1;i<=n;i++)
    		ans+=dp[i][1][m];
    	printf("%lld",ans);
    	return 0;
    }
    

    然后我们就A了这道题。。。
    。。。
    。。。
    吗?
    交上去之后发现毒瘤出题人要求高精度,那就重写吧,但是可读性来说绝对是上面的好。

    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    struct BigInt{
    	int b[50],rear;
    } dp[81][81][81],a[81][81];
    void init(BigInt &x){
    	for(int i=0;i<50;i++){
    		x.b[i]=0;
    	}
    	x.rear=0;
    	return;
    }
    BigInt operator +(const BigInt x,const BigInt y){
    		int flag=0,i;BigInt ans;
    		init(ans);
    		for(i=0;i<=max(x.rear,y.rear);i++){
    			ans.b[i]=x.b[i]+y.b[i]+flag;
    			flag=ans.b[i]/10;
    			ans.b[i]%=10;
    		}
    		if(flag){
    			ans.b[i]=1;
    			ans.rear=1+max(x.rear,y.rear);
    		}
    		else ans.rear=max(x.rear,y.rear);
    		return ans;
    	}
    BigInt operator *(const BigInt x,const BigInt y){
    	int flag=0,i,j;BigInt ans;
    	init(ans);
    	for(i=0;i<=x.rear;i++){
    		for(j=0;j<=y.rear;j++){
    			ans.b[i+j]+=x.b[i]*y.b[j];
    		}
    	}
    	ans.rear=x.rear+y.rear;
    	for(i=0;i<=x.rear+y.rear;i++){
    		ans.b[i]+=flag;
    		flag=ans.b[i]/10;
    		ans.b[i]%=10;
    	}
    	ans.rear=i;
    	if(flag) ans.b[i]=flag;
    	else ans.rear--;
    	return ans;
    }
    BigInt tran(char x[50]){
    	BigInt y;
    	int l=strlen(x)-1;
    	for(int i=0;i<=l;i++)
    		y.b[l-i]=x[i]-'0';
    	y.rear=l;
    	return y;
    }
    BigInt Max(BigInt x,BigInt y){
    	if(x.rear>y.rear) return x;
    	else if(y.rear>x.rear) return y;
    	else{
    		for(int i=x.rear;i>=0;i--){
    			if(x.b[i]>y.b[i]) return x;
    			else if(y.b[i]>x.b[i]) return y;
    		}
    	}
    	return x;
    }
    void print(BigInt x){
    	for(int i=x.rear;i>=0;i--)
    		printf("%d",x.b[i]);
    }
    BigInt pow[81],two;
    int main(){
    	int i,j,n,m,k;
    	BigInt ans;
    	char x[50];
    	init(ans);
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)
    		for(j=1;j<=m;j++){
    			scanf("%s",x);
    			a[i][j]=tran(x);
    		}
    	pow[0].b[0]=1;pow[0].rear=0;
    	two.rear=0;two.b[0]=2;
    	for(i=1;i<=m;i++){
    		pow[i]=two*pow[i-1];
    	}
    	for(i=1;i<=n;i++){
    		for(j=1;j<=m;j++){
    			dp[i][j][j]=a[i][j]*pow[m];
    			for(k=j-1;k;k--){
    				dp[i][k][j]=Max(a[i][k]*pow[m-(j-k)]+dp[i][k+1][j],a[i][j]*pow[m-(j-k)]+dp[i][k][j-1]);
    			}
    		}
    	}
    	for(i=1;i<=n;i++)
    		ans=ans+dp[i][1][m];
    	print(ans);
    	return 0;
    }
    

    高精度题最好用结构体写,函数封装一下,可以使代码更简洁。

  • 相关阅读:
    django--orm操作
    路由
    django ----视图和路由
    DJango 前三天小结
    JQuery----操作01
    前端---JQuery初识
    前端----jsDOM
    前端---js02
    前端-----js
    面向对象
  • 原文地址:https://www.cnblogs.com/landmine-sweeper/p/13673110.html
Copyright © 2020-2023  润新知