矩阵dp
矩阵dp
这里是矩阵dp,不是矩阵乘法优化dp。
矩阵上的dp好像也没什么特殊的?大概有一个套路就是从上向下,从左向右进行dp吧。
首先第一道题好像不是矩阵dp...
1005 矩阵取数游戏:https://www.luogu.org/problemnew/show/P1005
题意概述:在一个矩阵里面取数,每次从每行的左或右取一个数,并乘以$2^i$,$i$是取数的次数。直到取完为止,求最大得分。
看起来是道非常复杂的矩阵dp题,理性分析一下可以发现每一行之间并没有什么关系,所以分行做完以后再加起来就可以啦。那么在每一行内怎么做呢?像区间dp一样,用$dp[i][j]$表示取到剩下区间$[i][j]$时的最大得分。
然后再加上高精...
# include <cstdio>
# include <iostream>
# include <cstring>
# define R register int using namespace std; int n,m,j;
int len,a[][];
int dp[][][];
int P[][],ansc[][],ans[];
int t[]; void add (int *a,int *b)
{
len=max(a[],b[])+;
for (R i=;i<=len;++i)
{
a[i]+=b[i];
a[i+]+=a[i]/;
a[i]%=;
}
while (a[len]==&&len) len--;
a[]=len;
} void M (int *a,int *b)
{
if(a[]>b[]) return ;
if(b[]>a[])
{
len=b[];
for (R i=;i<=len;++i)
a[i]=b[i];
return ;
}
len=a[];
for (R i=len;i>=;--i)
{
if(a[i]>b[i]) return ;
if(a[i]==b[i]) continue;
if(a[i]<b[i])
{
for (R j=;j<=len;++j)
a[j]=b[j];
return ;
}
}
} void mul(int *a,int *b,int c)
{
len=b[];
for (R i=;i<=len;++i)
a[i]=b[i]*c;
len+=;
for (R i=;i<=len;++i)
a[i+]+=a[i]/,a[i]%=;
while (a[len]==&&len) len--;
a[]=len;
} int main()
{
scanf("%d%d",&n,&m);
P[][]=P[][]=;
for (R i=;i<=;++i)
{
P[i][]=P[i-][]*;
len=P[i][];
for (R j=;j<=len;++j)
P[i][j]=P[i-][j]*;
for (R j=;j<=len;++j)
P[i][j+]+=P[i][j]/,P[i][j]%=;
while (P[i][len]==&&len) len--;
P[i][]=len;
}
for (R i=;i<=n;++i)
for (R j=;j<=m;++j)
scanf("%d",&a[i][j]);
for (R c=;c<=n;++c)
{
memset(dp,,sizeof(dp));
memset(ansc,,sizeof(ansc));
for (R k=m;k>=;k--)
for (R i=;i+k-<=m;i++)
{
j=i+k-;
memset(t,,sizeof(t));
mul(t,P[m-k+],a[c][i]);
add(t,dp[i][j]);
M(dp[i+][j],t);
memset(t,,sizeof(t));
mul(t,P[m-k+],a[c][j]);
add(t,dp[i][j]);
M(dp[i][j-],t);
}
for (R i=;i<=m;++i)
{
mul(ansc[i],P[m],a[c][i]);
add(ansc[i],dp[i][i]);
}
for (R i=;i<=m;++i)
M(ansc[],ansc[i]);
add(ans,ansc[]);
}
len=ans[];
for (R i=len;i>=;--i)
printf("%d",ans[i]);
if(len==) printf("");
return ;
}
矩阵取数游戏
我发现我对矩阵这两个字大概是有什么误解吧,有的题不知道怎么归类,因为开了二维数组就进到矩阵dp这一栏里了?
垃圾陷阱:https://www.luogu.org/problemnew/show/P1156
题意概述:奶牛掉进了垃圾井,当有垃圾扔下来时,他可以吃掉,也可以堆放起来,问最短的出去时间,如果出不去,求最长的存活时间。
这道题有一个坑点,奶牛的生命值为0时还可以吃东西,堆垃圾,甚至跑出去...
用dp[i][j]表示用前i个物品,堆放高度为j时的最大生命值(能活到什么时候)。如果将一个垃圾堆起来,那它并不能续命,所以$dp[i][j]=max(dp[i-1][ j-h[i] ],dp[i][j])$;如果把垃圾吃了,那可以续一点命,但是不能增加高度,所以$dp[i][j]=max(dp[i][j],dp[i-1][j]+f[a])$.再处理一下边界就可以啦。
# include <cstdio>
# include <iostream>
# include <algorithm>
# include <cstring> using namespace std; struct rubbi
{
int t,f,h;
}a[]; bool cmp(rubbi a,rubbi b)
{
return a.t<b.t;
} int d,g,ans=1e7;
int dp[][]; int main()
{
int S=;
scanf("%d%d",&d,&g);
for (int i=;i<=g;i++)
scanf("%d%d%d",&a[i].t,&a[i].f,&a[i].h);
sort(a+,a++g,cmp);
memset(dp,-,sizeof(dp));
dp[][]=;
for (int i=;i<=g;i++)
for (int j=;j<=d+;j++)
{
if(dp[i-][j-a[i].h]!=-&&j>=a[i].h&&dp[i-][j-a[i].h]>=a[i].t)
dp[i][j]=max(dp[i][j],dp[i-][j-a[i].h]);
if(dp[i-][j]!=-&&dp[i-][j]>=a[i].t) dp[i][j]=max(dp[i][j],dp[i-][j]+max(a[i].f,));
if(dp[i][j]!=-&&j>=d)
{
ans=min(ans,a[i].t);
}
if(dp[i][j]!=-)
S=max(S,dp[i][j]);
}
if(ans!=1e7)
printf("%d",ans);
else
printf("%d",S);
return ;
}
垃圾陷阱
奶牛浴场:https://www.luogu.org/problemnew/show/P1578
题意概述:在一个有障碍的矩形中选出一个最大的子矩形,使得内部没有障碍点 (边界上可以有) 。
以前就做过这道题目,结果今天一看被Hack了,才发现之前学的那个方法不大对...这道题有两种做法,但是因为这题的障碍较少,所以第一种方法比较优也比较快。
https://wenku.baidu.com/view/728cd5126edb6f1aff001fbb.html (扔完链接就跑路)
具体实现就不写了,毕竟论文讲的非常详细非常好,记录一下要注意的地方吧。
·在矩形的四个顶点分别建立虚点。
·有的矩形是左边有一个障碍点,有的是右边有一个障碍点,所以正着做一遍,反着做一遍。
·有的矩形左右都没有障碍点,所以按照另一维坐标排序,将左右边界定为矩形的两边再统计一次答案。
·如果某一个障碍点和这次扫描的起点纵坐标相同就直接break。感觉好像会把一些合法情况排除掉呢,但是并不会。因为这两个点现在到了一条直线上,如果有合法答案必然会作为上边界或下边界,一个矩形有四个顶点,所以这样的情况必然还会在其他时间被枚举到。
# include <cstdio>
# include <iostream>
# include <algorithm>
# define R register int using namespace std; int l,w,n,h,t,le,ans=;
struct nod
{
int x,y;
}P[]; bool cmp1(nod a,nod b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
} bool cmp2(nod a,nod b)
{
return a.y<b.y;
} int main()
{
scanf("%d%d",&l,&w);
scanf("%d",&n);
for (R i=;i<=n;++i)
scanf("%d%d",&P[i].x,&P[i].y);
P[n+].x=; P[n+].y=;
P[n+].x=l; P[n+].y=;
P[n+].x=; P[n+].y=w;
P[n+].x=l; P[n+].y=w;
sort(P+,P++n,cmp1);
for (R i=;i<=n+;++i)
{
h=,t=w;
le=P[i].x;
for (R j=i+;j<=n+;++j)
{
if(P[j].x==le) continue;
ans=max(ans,(t-h)*(P[j].x-le));
if(P[j].y==P[i].y) break;
if(P[j].y>P[i].y) t=min(t,P[j].y);
if(P[j].y<P[i].y) h=max(h,P[j].y);
}
}
for (R i=n+;i>=;--i)
{
h=,t=w;
le=P[i].x;
for (R j=i-;j>=;j--)
{
if(P[j].x==le) continue;
ans=max(ans,(t-h)*(le-P[j].x));
if(P[j].y==P[i].y) break;
if(P[j].y>P[i].y) t=min(t,P[j].y);
if(P[j].y<P[i].y) h=max(h,P[j].y);
}
}
sort(P+,P++n,cmp2);
for (R i=;i<=n+;++i)
ans=max(ans,l*(P[i+].y-P[i].y));
printf("%d",ans);
return ;
}
奶牛浴场
[HAOI2007]理想的正方形:https://www.luogu.org/problemnew/show/P2216
题意概述:在一个a*b的矩形中选出一个n*n的正方形,使得正方形内的最大值减去最小值最小,求这个最小值。$1<=a,b<=2000$
二维线段树!显然是可以的,但是没有必要。观察数据范围可以发现$O(N^3)$的暴力就可以跑过去,这就给了我们很大的启发,可以只统计区间的最大最小值,行与行之间暴力统计,既然是要求区间的最大最小值就可以用单调队列啦。还有一个很不错的做法可以进一步提高程序速度,拒绝暴力统计,在单调队列的基础上再开行的单调队列,就$N^2$了,但是感觉挺麻烦的就没写。
// luogu-judger-enable-o2
# include <cstdio>
# include <cstring>
# include <iostream>
# define R register int
# define inf using namespace std; const int maxa=;
struct nod
{
int key,val;
};
nod q1[maxa],q2[maxa];
int a,b,n,h1,h2,t1,t2,big,sma;
int g[maxa][maxa];
int minn[maxa][maxa],maxx[maxa][maxa];
int ans=inf; void ins1 (int i,int j)
{
int x=g[i+n-][j+n-];
while (q1[h1].key<j&&h1<=t1) h1++;
while (q1[t1].val<=x&&h1<=t1) t1--;
q1[++t1].key=j+n-;
q1[t1].val=x;
} void ins2 (int i,int j)
{
int x=g[i+n-][j+n-];
while (q2[h2].key<j&&h2<=t2) h2++;
while (q2[t2].val>=x&&h2<=t2) t2--;
q2[++t2].key=j+n-;
q2[t2].val=x;
} int main()
{
scanf("%d%d%d",&a,&b,&n);
for (R i=;i<=a;++i)
for (R j=;j<=b;++j)
scanf("%d",&g[i][j]);
for (R i=;i<=a;++i)
for (R j=;j<=b;++j)
minn[i][j]=inf;
for (R i=-n;i<=a-n+;++i)
{
memset(q1,,sizeof(q1));
h1=h2=;
memset(q2,,sizeof(q2));
t1=t2=;
for (R j=-n;j<=b-n+;++j)
{
ins1(i,j);
ins2(i,j);
if(j<) continue;
maxx[i+n-][j]=q1[h1].val;
minn[i+n-][j]=q2[h2].val;
if(i<) continue;
big=,sma=inf;
for (int k=;k<=n;++k)
big=max(big,maxx[i+k-][j]),sma=min(sma,minn[i+k-][j]);
ans=min(ans,big-sma);
}
}
printf("%d",ans);
return ;
}
[HAOI2007]理想的正方形
--shzr
矩阵dp的更多相关文章
- hdu 4975 最大流问题解决队伍和矩阵,利用矩阵dp优化
//刚開始乱搞. //网络流求解,假设最大流=全部元素的和则有解:利用残留网络推断是否唯一, //方法有两种,第一种是深搜看看是否存在正边权的环.见上一篇4888 //至少四个点构成的环,另外一种是用 ...
- hdu4975 网络流解方程组(网络流+dfs判环或矩阵DP)
http://acm.hdu.edu.cn/showproblem.php?pid=4975 A simple Gaussian elimination problem. Time Limit: 20 ...
- Poj 2411 Mondriaan's Dream(压缩矩阵DP)
一.Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, ...
- hdu 4975 最大流解决行列和求矩阵问题,用到矩阵dp优化
//刚开始乱搞. //网络流求解,如果最大流=所有元素的和则有解:利用残留网络判断是否唯一, //方法有两种,第一种是深搜看看是否存在正边权的环,见上一篇4888 //至少四个点构成的环,第二种是用矩 ...
- lightOJ 1172 Krypton Number System(矩阵+DP)
题目链接:http://lightoj.com/volume_showproblem.php?problem=1172 题意:一个n进制(2<=n<=6)的数字,满足以下条件:(1)至少包 ...
- POJ2778&HDU2243&POJ1625(AC自动机+矩阵/DP)
POJ2778 题意:只有四种字符的字符串(A, C, T and G),有M中字符串不能出现,为长度为n的字符串可以有多少种. 题解:在字符串上有L中状态,所以就有L*A(字符个数)中状态转移.这里 ...
- bzoj1009 KMP+矩阵dp
https://www.lydsy.com/JudgeOnline/problem.php?id=1009 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(<=Xi<=), ...
- ZZNU 2182 矩阵dp (矩阵快速幂+递推式 || 杜教BM)
题目链接:http://47.93.249.116/problem.php?id=2182 题目描述 河神喜欢吃零食,有三种最喜欢的零食,鱼干,猪肉脯,巧克力.他每小时会选择一种吃一包. 不幸的是,医 ...
- Luogu3702 SDOI2017 序列计数 矩阵DP
传送门 不考虑质数的条件,可以考虑到一个很明显的$DP:$设$f_{i,j}$表示选$i$个数,和$mod\ p=j$的方案数,显然是可以矩阵优化$DP$的. 而且转移矩阵是循环矩阵,所以可以只用第一 ...
随机推荐
- 数据库中存储日期的字段类型到底应该用varchar还是datetime
将数据库中存储时间的数据类型改为varchar(),这时最好让这些时间是数据库中自动生成的(一个没有格式的输入也可能会导致输出错误),因为存储类型为varchar(),所以获取到的值也就被认为是一个字 ...
- net 反射30分钟速成
概述 什么是反射 Reflection,中文翻译为反射. 这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’.‘模块(Module)’. ...
- Netty接收到一个请求但是代码段执行了两次
这是因为HttpRequestDecoder把请求拆分成HttpRequest和HttpContent两部分, 所以在建立连接的时候建立了两次.
- Python 多线程、多进程 (一)之 源码执行流程、GIL
Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...
- js实现禁止右键 禁止f12 查看源代码
document.oncontextmenu = function () { return false; }; document.onkeydown = function () { if (windo ...
- Python爬虫入门教程石家庄链家租房数据抓取
1. 写在前面 这篇博客爬取了链家网的租房信息,爬取到的数据在后面的博客中可以作为一些数据分析的素材.我们需要爬取的网址为:https://sjz.lianjia.com/zufang/ 2. 分析网 ...
- Git冲突与解决方法
1.git冲突的场景 情景一:多个分支代码合并到一个分支时: 情景二:多个分支向同一个远端分支推送代码时: 实际上,push操作即是将本地代码merge到远端库分支上. 关于push和pull其实就分 ...
- MaxScript与外部程序通讯
最近项目要求通过java给max发送任务指令,max接收指令执行任务,并且返回执行的结果.不管为什么会有这样的需求,有就要去实现. 1.OLE开启 Max本身提供了一个方式,它可以将自己注册成一个Ol ...
- Origin绘制双Y轴图的方法
1.已有数据绘图如下,其中网络流量的单位是M Bytes/s,与另外两组数据的单位(时间)不同,现在要为其添加右侧Y轴. 2.首先选中该图像,找到工具条,点击第三个按钮“Add Right-Y Lay ...
- C#多线程的用法8-线程间的协作AutoResetEvent
AutoResetEvent自动重置事件,与ManualResetEvent是相对的而言.它同样用于线程间同步,请对照<C#多线程的用法7-线程间的协作ManualResetEvent>进 ...