http://www.tsinsen.com/ViewGProblem.page?gpid=A1198
试题来源
NOIP2007 提高组
问题描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:
1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2i,其中i表示第i次取数(从1开始编号);
4. 游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入格式
输入包括n+1行:
第1行为两个用空格隔开的整数n和m。
第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。
输出格式
输出包含1行,为一个整数,即输入矩阵取数后的最大得分。
样例输入
2 3
1 2 3
3 4 2
样例输出 82
样例输入
1 4
4 5 0 5
样例输出 122
样例输入
2 10
96 56 54 46 86 12 23 88 80 43
16 95 18 29 30 53 88 83 64 67
样例输出 316994
数据规模和约定
60%的数据满足:1<=n, m<=30, 答案不超过10^16
100%的数据满足:1<=n, m<=80, 0<=aij<=1000。
思路,首先进行了dp,
用f[i][j]表示区间[i, j]的最优解 f[i][j] = max(a[i] + 2 * f[i+1][j], a[j] + 2 * f[i][j-1]);//直接计算数的权值
但是后面的一直没有正确,原来是数字太大,超过了long long。
unsigned int 0~4294967295 (10位数,4e9)
int -2147483648~2147483647 (10位数,2e9 2^31 - 1)
long long: -9223372036854775808~9223372036854775807 (19位数, 9e18 ) 2^63 - 1
unsigned long long:0~18446744073709551615 (20位数,1e19) 2^64 - 1
这里介绍_int128, 只能gcc使用。
这种类型只有部分GCC编译器支持。经测试,正常运算与普通int无异,但是输入输出时无论是cin,cout还是printf都会报错,所以必须自己写输入输出函数。
定义时与别的数据类型并没有什么区别
eg: __int128 a,b,c;
输入暂时采用字符串读入方式。
#include<iostream>
#include<cstdio>
#include<set>
#include<vector>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<bits/stdc++.h>
using namespace std;
//typedef long long ll;
#define ll __int128
int m,n;
int iputs[90][90];
ll dp[90][90];
ll res;
int id;
void print(ll x) {
if (x==0) return;
if (x) print(x/10);
putchar(x%10+'0');
}
void scan(ll &x) { //输入
x = 0;
int f = 1;
char ch;
if((ch = getchar()) == '-') f = -f;
else x = x*10 + ch-'0';
while((ch = getchar()) >= '0' && ch <= '9')
x = x*10 + ch-'0';
x *= f;
}
ll DP(int a,int b) {
//cout<<a<<"---"<<b<<endl;
if(dp[a][b]>=0)
return dp[a][b];
if(a==b) {
return dp[a][b]=iputs[id][a];
}
ll imax1,imax2;
imax1=DP(a+1,b);
dp[a+1][b]=imax1;
imax2=DP(a,b-1);
dp[a][b-1]=imax2;
imax1=iputs[id][a]+2*imax1;
imax2=iputs[id][b]+2*imax2;
if(imax1>=imax2) {
return dp[a][b]=imax1;
} else {
return dp[a][b]=imax2;
}
}
int main() {
ll DP(int a,int b);
void scan(ll &x);
void print(ll x);
cin>>n>>m;
if(n==0||m==0) {
cout<<0<<endl;
return 0;
}
res=0;
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++)
cin>>(iputs[i][j]);
memset(dp,-1,sizeof(dp));
id=i;
res+=2*DP(0,m-1);
}
if(res==0) puts("0");
else print(res);
return 0;
}
关于int128读写
void read(__int128 &x)
{
x=0;
int f=1;
char ch;
if((ch=getchar())=='\n') x=x;
else if(ch=='-') f=-f;
else x=x*10+ch-'0';
while((ch=getchar())>='0' && ch<='9')
x=x*10+ch-'0';
x*=f;
}
void print(__int128 x)
{
if(x<0)
{
x=-x;
putchar('-');
}
if(x>9)
print(x/10);
putchar(x%10+'0');
}
另外,附加一个高精度模板
#include <algorithm> // max
#include <cassert> // assert
#include <cstdio> // printf,sprintf
#include <cstring> // strlen
#include <iostream> // cin,cout
#include <string> // string类
#include <vector> // vector类
using namespace std;
struct BigInteger {
typedef unsigned long long LL;
static const int BASE = 100000000;
static const int WIDTH = 8;
vector<int> s;
BigInteger& clean(){while(!s.back()&&s.size()>1)s.pop_back(); return *this;}
BigInteger(LL num = 0) {*this = num;}
BigInteger(string s) {*this = s;}
BigInteger& operator = (long long num) {
s.clear();
do {
s.push_back(num % BASE);
num /= BASE;
} while (num > 0);
return *this;
}
BigInteger& operator = (const string& str) {
s.clear();
int x, len = (str.length() - 1) / WIDTH + 1;
for (int i = 0; i < len; i++) {
int end = str.length() - i*WIDTH;
int start = max(0, end - WIDTH);
sscanf(str.substr(start,end-start).c_str(), "%d", &x);
s.push_back(x);
}
return (*this).clean();
}
BigInteger operator + (const BigInteger& b) const {
BigInteger c; c.s.clear();
for (int i = 0, g = 0; ; i++) {
if (g == 0 && i >= s.size() && i >= b.s.size()) break;
int x = g;
if (i < s.size()) x += s[i];
if (i < b.s.size()) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
}
BigInteger operator - (const BigInteger& b) const {
assert(b <= *this); // 减数不能大于被减数
BigInteger c; c.s.clear();
for (int i = 0, g = 0; ; i++) {
if (g == 0 && i >= s.size() && i >= b.s.size()) break;
int x = s[i] + g;
if (i < b.s.size()) x -= b.s[i];
if (x < 0) {g = -1; x += BASE;} else g = 0;
c.s.push_back(x);
}
return c.clean();
}
BigInteger operator * (const BigInteger& b) const {
int i, j; LL g;
vector<LL> v(s.size()+b.s.size(), 0);
BigInteger c; c.s.clear();
for(i=0;i<s.size();i++) for(j=0;j<b.s.size();j++) v[i+j]+=LL(s[i])*b.s[j];
for (i = 0, g = 0; ; i++) {
if (g ==0 && i >= v.size()) break;
LL x = v[i] + g;
c.s.push_back(x % BASE);
g = x / BASE;
}
return c.clean();
}
BigInteger operator / (const BigInteger& b) const {
assert(b > 0); // 除数必须大于0
BigInteger c = *this; // 商:主要是让c.s和(*this).s的vector一样大
BigInteger m; // 余数:初始化为0
for (int i = s.size()-1; i >= 0; i--) {
m = m*BASE + s[i];
c.s[i] = bsearch(b, m);
m -= b*c.s[i];
}
return c.clean();
}
BigInteger operator % (const BigInteger& b) const { //方法与除法相同
BigInteger c = *this;
BigInteger m;
for (int i = s.size()-1; i >= 0; i--) {
m = m*BASE + s[i];
c.s[i] = bsearch(b, m);
m -= b*c.s[i];
}
return m;
}
// 二分法找出满足bx<=m的最大的x
int bsearch(const BigInteger& b, const BigInteger& m) const{
int L = 0, R = BASE-1, x;
while (1) {
x = (L+R)>>1;
if (b*x<=m) {if (b*(x+1)>m) return x; else L = x;}
else R = x;
}
}
BigInteger& operator += (const BigInteger& b) {*this = *this + b; return *this;}
BigInteger& operator -= (const BigInteger& b) {*this = *this - b; return *this;}
BigInteger& operator *= (const BigInteger& b) {*this = *this * b; return *this;}
BigInteger& operator /= (const BigInteger& b) {*this = *this / b; return *this;}
BigInteger& operator %= (const BigInteger& b) {*this = *this % b; return *this;}
bool operator < (const BigInteger& b) const {
if (s.size() != b.s.size()) return s.size() < b.s.size();
for (int i = s.size()-1; i >= 0; i--)
if (s[i] != b.s[i]) return s[i] < b.s[i];
return false;
}
bool operator >(const BigInteger& b) const{return b < *this;}
bool operator<=(const BigInteger& b) const{return !(b < *this);}
bool operator>=(const BigInteger& b) const{return !(*this < b);}
bool operator!=(const BigInteger& b) const{return b < *this || *this < b;}
bool operator==(const BigInteger& b) const{return !(b < *this) && !(b > *this);}
};
ostream& operator << (ostream& out, const BigInteger& x) {
out << x.s.back();
for (int i = x.s.size()-2; i >= 0; i--) {
char buf[20];
sprintf(buf, "%08d", x.s[i]);
for (int j = 0; j < strlen(buf); j++) out << buf[j];
}
return out;
}
istream& operator >> (istream& in, BigInteger& x) {
string s;
if (!(in >> s)) return in;
x = s;
return in;
}
const int maxn=105;
#define ll BigInteger
int n,m;
int a[maxn];
ll dp[maxn][maxn];
ll ans=0;
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[j];
}
for(int x=0;x<m;x++){
for(int y=0;y<m;y++){
dp[x][y]=0;
}
}
ll mi=1;
for(int j=0;j<m;j++)mi*=2;
for(int j=0;j<m;j++){
dp[j][j]=mi*a[j];
}
for(int len=2;len<=m;len++){
mi/=2;//chu2
for(int j=0;j<=m-len;j++){//遍历每一个大小为len的区间
dp[j][j+len-1]=max(dp[j+1][j+len-1]+mi*a[j],dp[j][j+len-2]+mi*a[j+len-1]);
}
}
ans+=dp[0][m-1];
}
cout<<ans<<endl;
return 0;
}