与高精死杠的几天——记两道简单的高精dp
(同样也是noip往年的题
1.矩阵取数游戏
\(\mathcal{SOLUTION}:\)
通过对题目条件的分析,我们可以发现,每一行取数对答案的影响是互相独立,互不影响的。所以我们可以从一个1*m的矩阵开始研究:
因为每次取数只能取最左边或最右边,这又简化了我们的思路:
我们设\(dp[i][j][k]\)表示第i轮取数后,还没有取的区间范围为[j,k],形象一点的话:k-j+1(区间长度)=m-i;
考虑转移:对于\(dp[i][j][k]\),我们要考虑,第i次取数取的哪个数:
显然取完后的区间变为[j,k],那么因为只能取最左边或最右边,所以第i次取走的数,可能是在j再左边一个,也就是j-1的位置,也可能是k右边的位置,即k+1的位置。因此我们基本的转移方程就出来了:
\(dp[i][j][k]=max(dp[i-1][j-1][k]+a[j-1]*2^i,dp[i-1][j][k+1]+a[k+1]*2^i);\)
k的位置不需要for枚举,每次取数时,区间长度是固定的,因此可以只枚举i,j,k=j+m-i-1;
当k>m时,break;
如果取到第m轮的话,我们的区间变成了[a,a-1]的形式,这显然是不行的,因此我们需要在第m-1轮时,顺带处理第m轮的情况(反正取到m-1轮以后只剩一个数了嘿,于是:
\(This=max(This,dp[m-1][j][j]+a[j]*2^m)\);
找到This的最大值。
对每一行都dp一遍,然后将最大值加和,即是最终答案。
但是如果你只是这样的话,只能得到60pts的好成绩;
m<=80,于是我们单单是\(2^{80}\),就狠狠地爆掉了long long,所以这道题很烦人的要写高精:
它们分别是:
\(\mathbb{A}.\)高精加高精
\(\mathbb{B}.\)高精乘单精
\(\mathbb{C}.\)高精MAX
可以提前预处理出\(2^m\)内的所有2的整数次方,避免重复计算。
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int ans=0;
char last=' ',ch=getchar();
while(ch>'9'||ch<'0') last=ch,ch=getchar();
while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
if(last=='-') ans=-ans;
return ans;
}
int n,m;
int A[100][100];
struct GJ{
int C[50],len;
GJ() {
memset(C,0,sizeof(C));
len=0;
}
//n->1 max->min
};
GJ M[81];
GJ Mul(GJ a,int b) {
int lena=a.len;
GJ c;
for(int i=1;i<=lena;i++)
c.C[i]=a.C[i]*b;
for(int i=1;i<=lena;i++)
if(c.C[i]>=10) {
c.C[i+1]+=c.C[i]/10;
c.C[i]%=10;
}
int lenc=lena;
while(c.C[lenc+1]>0) {
lenc++;
c.C[lenc+1]+=c.C[lenc]/10;
c.C[lenc]%=10;
}
c.len=lenc;
return c;
}
GJ Max(GJ a,GJ b) {//gaojing max
if(a.len<b.len)
return b;
if(b.len>a.len)
return a;
for(int i=a.len;i>=1;i--) {
if(a.C[i]>b.C[i])
return a;
if(b.C[i]>a.C[i])
return b;
}
return a;
}
GJ Sum(GJ a,GJ b) {
int len = max(a.len,b.len);
GJ c;
for(int i=1;i<=len;i++)
c.C[i]=a.C[i]+b.C[i];
for(int i=1;i<=len;i++) {
if(c.C[i]>=10) {
c.C[i+1]+=c.C[i]/10;
c.C[i]%=10;
}
}
if(c.C[len+1]>0) len++;
c.len=len;
return c;
}
void ych() {
GJ E,AA;
E.len=1;
E.C[1]=1;
int k=1;
while(k<=m) {
E=Mul(E,2);
M[k]=E;
k++;
}
}
GJ dp[82][82][82];
GJ ans,This;
void clear() {
memset(dp,0,sizeof(dp));
memset(This.C,0,sizeof(This.C));
}
//2+8+24+4+12+32
int main() {
n=read();
m=read();
ych();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
A[i][j]=read();
for(int line = 1;line <= n; line ++ ) {
clear();
for(int i=1;i<m;i++) {//i 轮
for(int j=1;j<=m;j++) {//区间[j,j+m-i-1]
if(j+m-i-1>m) break;
dp[i][j][j+m-i-1]=Max(Sum(dp[i-1][j-1][j+m-i-1],
Mul(M[i],A[line][j-1])),
Sum(dp[i-1][j][j+m-i],Mul(M[i],A[line][j+m-i])));
if(i==m-1)
This=Max(This,Sum(dp[i][j][j],Mul(M[m],A[line][j])));
}
}
ans=Sum(ans,This);
}
if(ans.len==0)
puts("0");
for(int i=ans.len;i>=1;i--)
printf("%d",ans.C[i]);
return 0;
}
但是这样还是很难过,你会发现,你在luogu上TLE了两个点。发现dp数组中,第一维i是没有用的,所以我们可以把它删去,这样dp每一行前,清空的时间就会减少(这个减少真的是革命性的1.2s->216ms)
\(\mathcal{CODE}:\)
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int ans=0;
char last=' ',ch=getchar();
while(ch>'9'||ch<'0') last=ch,ch=getchar();
while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
if(last=='-') ans=-ans;
return ans;
}
int n,m;
int A[100][100];
struct GJ{
int C[50],len;
GJ() {
memset(C,0,sizeof(C));
len=0;
}
//n->1 max->min
};
GJ M[81];
GJ Mul(GJ a,int b) {
int lena=a.len;
GJ c;
for(int i=1;i<=lena;i++) {
c.C[i]+=a.C[i]*b;
if(c.C[i]>=10) {
c.C[i+1]+=c.C[i]/10;
c.C[i]%=10;
}
}
int lenc=lena;
while(c.C[lenc+1]>0) {
lenc++;
c.C[lenc+1]+=c.C[lenc]/10;
c.C[lenc]%=10;
}
c.len=lenc;
return c;
}
GJ Max(GJ a,GJ b) {
if(a.len<b.len)
return b;
if(b.len>a.len)
return a;
for(int i=a.len;i>=1;i--) {
if(a.C[i]>b.C[i])
return a;
if(b.C[i]>a.C[i])
return b;
}
return a;
}
GJ Sum(GJ a,GJ b) {
int len = max(a.len,b.len);
GJ c;
for(int i=1;i<=len;i++) {
c.C[i]+=a.C[i]+b.C[i];
if(c.C[i]>=10) {
c.C[i+1]+=c.C[i]/10;
c.C[i]%=10;
}
}
if(c.C[len+1]>0) len++;
c.len=len;
return c;
}
void ych() {
GJ E;
E.len=1; E.C[1]=1;
int k=1;
while(k<=m) {
E=Mul(E,2);
M[k]=E;
k++;
}
}
GJ dp[82][82];
GJ ans,This;
void clear() {
memset(dp,0,sizeof(dp));
This.len=-1;
}
int main() {
n=read();
m=read();
ych();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
A[i][j]=read();
for(int line = 1;line <= n; line ++ ) {
clear();
for(int i=1;i<m;i++) {//i 轮
for(int j=1;j<=m;j++) {//区间[j,j+m-i-1]
if(j+m-i-1>m) break;
dp[j][j+m-i-1]=Max(Sum(dp[j-1][j+m-i-1],
Mul(M[i],A[line][j-1])),
Sum(dp[j][j+m-i],Mul(M[i],A[line][j+m-i])));
if(i==m-1)
This=Max(This,Sum(dp[j][j],Mul(M[m],A[line][j])));
}
}
ans=Sum(ans,This);
}
if(ans.len==0)
puts("0");
for(int i=ans.len;i>=1;i--)
printf("%d",ans.C[i]);
return 0;
}
2.乘积最大:
\(\mathcal{SOLUTION}:\)
\(dp[i][j]\)表示前i位,j个乘号的最大乘积是多少
\(dp[i][j]\),考虑枚举上一个乘号放在了哪里。假设放在了K,那么此时到i的乘积就为:\(dp[K][j-1]*num[K+1][i]\)(其中\(num[K+1][i]\)表示从第K+1位到第i位的数字),那么从1~i-1枚举,求一个最大乘积,即:
\(dp[i][j]=max\{dp[k][j-1]*num[k+1][i]\}\)
初始状态时:\(dp[i][0]=num[1][i]\)
于是写个高精就可以了
\(\mathcal{CODE}:\)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,K;
char N[50];
struct GJ {
int C[50],len;
GJ() {
memset(C,0,sizeof(C));
len=0;
}
//max->min n->1
};
GJ num[41][41];
void pre() {
int len=strlen(N+1);
for(int i=1;i<=len;i++) {
for(int j=i;j<=len;j++) {
int cnt=0;
for(int k=j;k>=i;k--) {
num[i][j].C[++cnt]=N[k]-'0';
}
while(num[i][j].C[cnt]==0&&cnt!=1) cnt--;
num[i][j].len=cnt;
}
}
}
GJ Mul(GJ a,GJ b) {
int lena=a.len,lenb=b.len;
GJ c;
for(int i=1;i<=lena;i++)
for(int j=1;j<=lenb;j++)
c.C[i+j-1]+=a.C[i]*b.C[j];
for(int i=1;i<lena+lenb-1;i++)
if(c.C[i]>=10) {
c.C[i+1]+=c.C[i]/10;
c.C[i]%=10;
}
int len=lena+lenb-1;
while(c.C[len]>=10) {
c.C[len+1]+=c.C[len]/10;
c.C[len]%=10;
len++;
}
c.len=len;
return c;
}
GJ Max(GJ a,GJ b) {
if(a.len<b.len)
return b;
if(b.len>a.len)
return a;
for(int i=a.len;i>=1;i--) {
if(a.C[i]>b.C[i])
return a;
if(b.C[i]>a.C[i])
return b;
}
return a;
}
GJ Sum(GJ a,GJ b) {
int len = max(a.len,b.len);
GJ c;
for(int i=1;i<=len;i++) {
c.C[i]+=a.C[i]+b.C[i];
if(c.C[i]>=10) {
c.C[i+1]+=c.C[i]/10;
c.C[i]%=10;
}
}
if(c.C[len+1]>0) len++;
c.len=len;
return c;
}
GJ dp[41][10];
int main() {
scanf("%d%d",&n,&K);
scanf("%s",N+1);
pre();
for(int i=1;i<=n;i++)
dp[i][0]=num[1][i];
for(int i=1;i<=n;i++) {
for(int j=1;j<=K;j++) {
for(int k=1;k<i;k++) {
dp[i][j]=Max(dp[i][j],Mul(dp[k][j-1],num[k+1][i]));
}
}
}
for(int i=dp[n][K].len;i>=1;i--)
printf("%d",dp[n][K].C[i]);
return 0;
}
\(\color{OrangeRed}{2}\color{Orange}{0}\color{Yellow}{1}\color{LimeGreen}{9}\color{RoyalBlue}{是}\color{Turquoise}{彩}\color{Plum}{色}\color{Gold}{的}\)
(玩梗态)
与高精死杠的几天——记两道简单的高精dp的更多相关文章
- [转帖] 中国SaaS死或生之二: ERP两大邪术,尽出歪门邪路 ---- 挺好玩的
中国SaaS死或生之二: ERP两大邪术,尽出歪门邪路 http://www.cniteyes.com/archives/33753 文章摘要:在数字化浪潮中,油腻ERP大叔的那些“歪门邪术” ...
- 磁盘IO过高时的处理办法 针对系统中磁盘IO负载过高的指导性操作
磁盘IO过高时的处理办法 针对系统中磁盘IO负载过高的指导性操作 主要命令:echo deadline > /sys/block/sda/queue/scheduler 注:以下的内容仅是提供参 ...
- OpenStack之Neutron分配VIP提供给两台虚拟机做高可用
一. 简单介绍 在openstack私有云平台的应用场景中,涉及多台虚拟机实例进行高可用的绑定,这里我们需要在云平台中提供一个IP给高可用场景切换,这里介绍keepalived + allow_add ...
- 《编写高质量代码:Web 前端开发修炼之道》 笔记与读后感
编写高质量代码:Web 前端开发修炼之道/曹刘阳著. —北京:机械工业出版社,2010.5 第一版 涉及到的知识点: 1. CSS Sprites 在国内很多人叫css精灵,是一种网页图片应用处理方式 ...
- 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存
原文:http://blog.csdn.net/heyewu4107/article/details/71009712 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存 问 ...
- 行内元素(inline标签)设置了行高为什么不生效,还是表现为父盒子的行高?行内元素行高问题终极解释
最近在看张鑫旭大佬的<css世界>,读到5.2.4 内联元素 line-height 的“大值特性” ,产生了疑惑, 在开发中确实也遇到了同样的问题,深入探究后得出结果,先说结论吧,论证 ...
- css实现内容不相同的左右两个div等高
问题提出 现在有两个div左右排列,但是两个div的内容不相同,如何设置两个div的css做到在两个div等高排列呢? 下面是网上找的3种实现方法,觉得很有代表性,所以索性收藏起来. 方法一 通过父元 ...
- 《代码整洁之道 中文版》高清 PDF 电子书下载
代码整洁之道.PDF 下载 代码整洁之道.PDF 中文版 高清 PDF 电子书下载 代码整洁之道下载 点我下载 作者简介 · · · · · · Robert C. Martin,Object ...
- 反斜杠在JSP中的两种不同的含义
/ 在不同条件下的不同含义 / 代表WEB应用的根路径的情况:/ 交给 Servlet容器来处理 请求转发时: request.getRequestDispatcher("/xxxx&quo ...
随机推荐
- Charles中文破解版下载安装及使用教程
下载地址:https://pan.baidu.com/s/1praYZAw23psZLi59hKJjqw 一. 简介及安装 Charles 是在 PC 端常用的网络封包截取工具,但它不仅仅能在pc端使 ...
- springMVC中的ModelAndView说明
ModelAndView 类别就如其名称所示,是代表了Spring Web MVC程式中呈现画面时所使用Model资料物件与View资料物件,由于Java程式中一次只能返回一个物件,所以ModelAn ...
- SRS之RTMP连接处理线程conn:接收客户端推流
由 SRS之RTMP的TCP线程 分析可知,SRS 接受客户端的连接后创建了一个线程:conn,用于处理与客户端的 RTMP 连接. 本文的分析是基于该配置文件的: listen 1935; max_ ...
- js版的虚线框
要求:实现鼠标点击面板的一个点,拖动到固定的位置,出现虚线框 样图: 具体的代码实现: js文件 <script> window.onload = function(){ docu ...
- selenium爬虫使用
1. 网页的打开 from selenium import webdriver import time driver = webdriver.Chrome(executable_path=r" ...
- LC 650. 2 Keys Keyboard
Initially on a notepad only one character 'A' is present. You can perform two operations on this not ...
- Ironic 裸金属管理服务的底层技术支撑
目录 文章目录 目录 底层技术支撑 DHCP NBP TFTP IPMI PXE & iPXE Cloud Init Linux 操作系统启动引导过程 底层技术支撑 PXE:预启动执行环境,支 ...
- SQLSERVER大批量数据快速导入Redis
目的 把单表近5千万的某单个字段导入到Redis,作为一个list存储. 方案一: 使用sqlcmd工具(sqlserver自带),直接生成命令在Redis-cli中执行. 方案一. 使用sqlcmd ...
- stegsolve---图片隐写查看器
今天做CTF隐写术的题偶然发现一隐写图片查看的神器------stegsolve,分享给大家 stegsolve下载地址:http://www.caesum.com/handbook/Stegsolv ...
- C++输入输出流加速器,关闭同步流,ios::sync_with_stdio(false)和 cin.tie(0)
leetcode练习时,总会发现运行时间短的代码都会有类似: static int x=[](){ std::ios::sync_with_stdio(false); cin.tie(NULL); ; ...