codevs 1166 矩阵取数游戏 (2007 noip)

本文介绍了一种使用高精度数据结构解决大型矩阵中寻找最大路径和的问题。该问题可通过动态规划逐步解决,每行独立计算最优路径,并通过自定义高精度数结构避免整数溢出。

首先可以判断这是一个dp,然后这个矩阵中各个行之间是没有联系的,所以可以一行一行地求出最优解然后通过循环加起来

不是一个特别难的dp 但是在数目问题上比较大 反正一看到平方啊这种东西应该差不多会溢出的……所以写一个高精度的结构体 就ok了!!


代码在这:

#include <bits/stdc++.h>
#include <iostream>

using namespace std;


const int maxn=85;
int n,m;
int temp[maxn][maxn];

const int power=4;
const int base=10000;
const int maxl=35;

struct num{
	int a[maxl];

	num(){
		memset(a,0,sizeof(a));
	}

	num operator + (const num &b){
		num c;
		c.a[0]=max(a[0],b.a[0]);
		for(int i=1;i<=c.a[0];++i){
			c.a[i]+=a[i]+b.a[i];
			c.a[i+1]+=c.a[i]/base;
			c.a[i]=c.a[i]%base;
		}
		if(c.a[c.a[0]+1]) ++c.a[0];
		return c;
	}

	num operator + (const int &b){
		a[1]+=b;
		int i=1;
		while(a[i]>=base){
			a[i+1]+=a[i]/base;
			a[i]%=base;
			i++;
		}
		while(a[a[0]+1]) ++a[0];
		return *this;
	}

	num operator = (int b){
		a[0]=0;
		while(b){
			a[0]++;
			a[a[0]]=b%base;
			b/=base;
		}
		return *this;
	}

	bool operator < (const num &b)const{
		if(a[0]<b.a[0]) return true;
		if(a[0]>b.a[0]) return false;
		for(int i=a[0];i>0;--i)
			if(a[i]!=b.a[i]) return a[i]<b.a[i];
		return false;
	}

	void print(){
		printf("%d",a[a[0]]);
		for(int i=a[0]-1;i>0;--i)
			printf("%0*d",power,a[i]);
		printf("\n");
	}
}ans,s[maxn][maxn];

int main()
{

	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			scanf("%d",&temp[i][j]);

	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
			s[j][j]=temp[i][j];
		int x;
		for(int j=1;j<=m-1;++j)
		for(int k=1;k<=m-j;++k){
			x=k+j;
			s[k][x]=max(s[k][x-1]+s[k][x-1]+temp[i][x],s[k+1][x]+s[k+1][x]+temp[i][k]);
		}
		ans=ans+s[1][m];
		ans=ans+s[1][m];
	}
	ans.print();
    return 0;
}

### NOIP 2007 提高组 P1005 矩阵游戏 C++ 解法 #### 动态规划思路解析 此问题的核心在于通过动态规划解决如何最大化矩阵后的总得分。定义状态 `f[i][j]` 表示当前子序列从位置 `i` 到位置 `j` 的最大得分,则转移方程可以表示为: ```cpp f[i][j] = max(f[i+1][j] + a[i] * pow(2, t), f[i][j-1] + a[j] * pow(2, t)); ``` 其中,`t` 是当前操作次,`a[i]` 和 `a[j]` 分别代表选的头尾元素。 由于涉及大运算,需采用自定义的大整类或者使用内置支持更大范围的据类型(如 `__int128`)。以下是具体实现方法[^2]。 --- #### 完整代码实现 以下是一个基于动态规划并考虑大处理的完整解决方案: ```cpp #include <bits/stdc++.h> using namespace std; struct Int128 { long long high; long long low; }; Int128 operator+(const Int128& a, const Int128& b) { Int128 res; res.low = a.low + b.low; res.high = a.high + b.high + (res.low >= 1e18 ? 1 : 0); res.low %= 1e18; return res; } Int128 operator*(const Int128& a, long long b) { Int128 res; res.low = a.low * b; res.high = a.high * b + (res.low / 1e18); res.low %= 1e18; return res; } bool operator<(const Int128& a, const Int128& b) { if (a.high != b.high) return a.high < b.high; return a.low < b.low; } Int128 max(const Int128& a, const Int128& b) { return a < b ? b : a; } void print(Int128 num) { if (num.high > 0) cout << num.high; printf("%018lld", num.low); } long long power[105]; Int128 dp[105][105]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; // Precompute powers of 2 power[0] = 1; for (int i = 1; i <= m; ++i) power[i] = power[i - 1] * 2; Int128 totalScore = {0, 0}; while (n--) { vector<long long> row(m + 1, 0); for (int i = 1; i <= m; ++i) cin >> row[i]; // Initialize DP table fill(&dp[0][0], &dp[m][m] + 1, Int128{0, 0}); for (int len = 1; len <= m; ++len) { for (int l = 1; l + len - 1 <= m; ++l) { int r = l + len - 1; if (len == 1) { dp[l][r].low = row[l] * power[m - len + 1]; } else { dp[l][r] = max( dp[l + 1][r] + Int128{(row[l] * power[m - len + 1]) / 1e18, (row[l] * power[m - len + 1]) % 1e18}, dp[l][r - 1] + Int128{(row[r] * power[m - len + 1]) / 1e18, (row[r] * power[m - len + 1]) % 1e18} ); } } } totalScore = totalScore + dp[1][m]; } print(totalScore); return 0; } ``` --- #### 关键点说明 1. **据结构设计** 使用自定义结构体 `Int128` 来存储大值,分别记录高位和低位部分以便于计算超大据的结果[^5]。 2. **幂次预处理** 预先计算好所有的 $2^i$ 幂次值存入组中以减少重复计算开销[^3]。 3. **边界条件处理** 当长度等于1时直接赋值对应单个元素乘以其权重作为初始状态;其他情况则依据前一步的状态更新当前最优解[^4]。 4. **最终结果输出** 对于超过常规类型的答案采分段打印方式展示全部有效位[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值