矩阵dp
这里是矩阵dp,不是矩阵乘法优化dp。
矩阵上的dp好像也没什么特殊的?大概有一个套路就是从上向下,从左向右进行dp吧。
首先第一道题好像不是矩阵dp...
1005 矩阵取数游戏:https://www.luogu.org/problemnew/show/P1005
题意概述:在一个矩阵里面取数,每次从每行的左或右取一个数,并乘以$2^i$,$i$是取数的次数。直到取完为止,求最大得分。
看起来是道非常复杂的矩阵dp题,理性分析一下可以发现每一行之间并没有什么关系,所以分行做完以后再加起来就可以啦。那么在每一行内怎么做呢?像区间dp一样,用$dp[i][j]$表示取到剩下区间$[i][j]$时的最大得分。
然后再加上高精...
1 # include <cstdio> 2 # include <iostream> 3 # include <cstring> 4 # define R register int 5 6 using namespace std; 7 8 int n,m,j; 9 int len,a[81][81]; 10 int dp[81][81][1000]; 11 int P[81][100],ansc[81][1000],ans[1000]; 12 int t[1000]; 13 14 void add (int *a,int *b) 15 { 16 len=max(a[0],b[0])+2; 17 for (R i=1;i<=len;++i) 18 { 19 a[i]+=b[i]; 20 a[i+1]+=a[i]/10; 21 a[i]%=10; 22 } 23 while (a[len]==0&&len) len--; 24 a[0]=len; 25 } 26 27 void M (int *a,int *b) 28 { 29 if(a[0]>b[0]) return ; 30 if(b[0]>a[0]) 31 { 32 len=b[0]; 33 for (R i=0;i<=len;++i) 34 a[i]=b[i]; 35 return ; 36 } 37 len=a[0]; 38 for (R i=len;i>=1;--i) 39 { 40 if(a[i]>b[i]) return ; 41 if(a[i]==b[i]) continue; 42 if(a[i]<b[i]) 43 { 44 for (R j=0;j<=len;++j) 45 a[j]=b[j]; 46 return ; 47 } 48 } 49 } 50 51 void mul(int *a,int *b,int c) 52 { 53 len=b[0]; 54 for (R i=1;i<=len;++i) 55 a[i]=b[i]*c; 56 len+=3; 57 for (R i=1;i<=len;++i) 58 a[i+1]+=a[i]/10,a[i]%=10; 59 while (a[len]==0&&len) len--; 60 a[0]=len; 61 } 62 63 int main() 64 { 65 scanf("%d%d",&n,&m); 66 P[0][0]=P[0][1]=1; 67 for (R i=1;i<=80;++i) 68 { 69 P[i][0]=P[i-1][0]*2; 70 len=P[i][0]; 71 for (R j=1;j<=len;++j) 72 P[i][j]=P[i-1][j]*2; 73 for (R j=1;j<=len;++j) 74 P[i][j+1]+=P[i][j]/10,P[i][j]%=10; 75 while (P[i][len]==0&&len) len--; 76 P[i][0]=len; 77 } 78 for (R i=1;i<=n;++i) 79 for (R j=1;j<=m;++j) 80 scanf("%d",&a[i][j]); 81 for (R c=1;c<=n;++c) 82 { 83 memset(dp,0,sizeof(dp)); 84 memset(ansc,0,sizeof(ansc)); 85 for (R k=m;k>=1;k--) 86 for (R i=1;i+k-1<=m;i++) 87 { 88 j=i+k-1; 89 memset(t,0,sizeof(t)); 90 mul(t,P[m-k+1],a[c][i]); 91 add(t,dp[i][j]); 92 M(dp[i+1][j],t); 93 memset(t,0,sizeof(t)); 94 mul(t,P[m-k+1],a[c][j]); 95 add(t,dp[i][j]); 96 M(dp[i][j-1],t); 97 } 98 for (R i=1;i<=m;++i) 99 { 100 mul(ansc[i],P[m],a[c][i]); 101 add(ansc[i],dp[i][i]); 102 } 103 for (R i=1;i<=m;++i) 104 M(ansc[1],ansc[i]); 105 add(ans,ansc[1]); 106 } 107 len=ans[0]; 108 for (R i=len;i>=1;--i) 109 printf("%d",ans[i]); 110 if(len==0) printf("0"); 111 return 0; 112 }
我发现我对矩阵这两个字大概是有什么误解吧,有的题不知道怎么归类,因为开了二维数组就进到矩阵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[102]; bool cmp(rubbi a,rubbi b) { return a.t<b.t; } int d,g,ans=1e7; int dp[102][200]; int main() { int S=10; scanf("%d%d",&d,&g); for (int i=1;i<=g;i++) scanf("%d%d%d",&a[i].t,&a[i].f,&a[i].h); sort(a+1,a+1+g,cmp); memset(dp,-1,sizeof(dp)); dp[0][0]=10; for (int i=1;i<=g;i++) for (int j=0;j<=d+40;j++) { if(dp[i-1][j-a[i].h]!=-1&&j>=a[i].h&&dp[i-1][j-a[i].h]>=a[i].t) dp[i][j]=max(dp[i][j],dp[i-1][j-a[i].h]); if(dp[i-1][j]!=-1&&dp[i-1][j]>=a[i].t) dp[i][j]=max(dp[i][j],dp[i-1][j]+max(a[i].f,0)); if(dp[i][j]!=-1&&j>=d) { ans=min(ans,a[i].t); } if(dp[i][j]!=-1) S=max(S,dp[i][j]); } if(ans!=1e7) printf("%d",ans); else printf("%d",S); return 0; }
奶牛浴场:https://www.luogu.org/problemnew/show/P1578
题意概述:在一个有障碍的矩形中选出一个最大的子矩形,使得内部没有障碍点 (边界上可以有) 。
以前就做过这道题目,结果今天一看被Hack了,才发现之前学的那个方法不大对...这道题有两种做法,但是因为这题的障碍较少,所以第一种方法比较优也比较快。
https://wenku.baidu.com/view/728cd5126edb6f1aff001fbb.html (扔完链接就跑路)
具体实现就不写了,毕竟论文讲的非常详细非常好,记录一下要注意的地方吧。
·在矩形的四个顶点分别建立虚点。
·有的矩形是左边有一个障碍点,有的是右边有一个障碍点,所以正着做一遍,反着做一遍。
·有的矩形左右都没有障碍点,所以按照另一维坐标排序,将左右边界定为矩形的两边再统计一次答案。
·如果某一个障碍点和这次扫描的起点纵坐标相同就直接break。感觉好像会把一些合法情况排除掉呢,但是并不会。因为这两个点现在到了一条直线上,如果有合法答案必然会作为上边界或下边界,一个矩形有四个顶点,所以这样的情况必然还会在其他时间被枚举到。
1 # include <cstdio> 2 # include <iostream> 3 # include <algorithm> 4 # define R register int 5 6 using namespace std; 7 8 int l,w,n,h,t,le,ans=0; 9 struct nod 10 { 11 int x,y; 12 }P[5009]; 13 14 bool cmp1(nod a,nod b) 15 { 16 if(a.x==b.x) return a.y<b.y; 17 return a.x<b.x; 18 } 19 20 bool cmp2(nod a,nod b) 21 { 22 return a.y<b.y; 23 } 24 25 int main() 26 { 27 scanf("%d%d",&l,&w); 28 scanf("%d",&n); 29 for (R i=1;i<=n;++i) 30 scanf("%d%d",&P[i].x,&P[i].y); 31 P[n+1].x=0; P[n+1].y=0; 32 P[n+2].x=l; P[n+2].y=0; 33 P[n+3].x=0; P[n+3].y=w; 34 P[n+4].x=l; P[n+4].y=w; 35 sort(P+1,P+5+n,cmp1); 36 for (R i=1;i<=n+4;++i) 37 { 38 h=0,t=w; 39 le=P[i].x; 40 for (R j=i+1;j<=n+4;++j) 41 { 42 if(P[j].x==le) continue; 43 ans=max(ans,(t-h)*(P[j].x-le)); 44 if(P[j].y==P[i].y) break; 45 if(P[j].y>P[i].y) t=min(t,P[j].y); 46 if(P[j].y<P[i].y) h=max(h,P[j].y); 47 } 48 } 49 for (R i=n+4;i>=1;--i) 50 { 51 h=0,t=w; 52 le=P[i].x; 53 for (R j=i-1;j>=1;j--) 54 { 55 if(P[j].x==le) continue; 56 ans=max(ans,(t-h)*(le-P[j].x)); 57 if(P[j].y==P[i].y) break; 58 if(P[j].y>P[i].y) t=min(t,P[j].y); 59 if(P[j].y<P[i].y) h=max(h,P[j].y); 60 } 61 } 62 sort(P+1,P+5+n,cmp2); 63 for (R i=1;i<=n+3;++i) 64 ans=max(ans,l*(P[i+1].y-P[i].y)); 65 printf("%d",ans); 66 return 0; 67 }
[HAOI2007]理想的正方形:https://www.luogu.org/problemnew/show/P2216
题意概述:在一个a*b的矩形中选出一个n*n的正方形,使得正方形内的最大值减去最小值最小,求这个最小值。$1<=a,b<=2000$
二维线段树!显然是可以的,但是没有必要。观察数据范围可以发现$O(N^3)$的暴力就可以跑过去,这就给了我们很大的启发,可以只统计区间的最大最小值,行与行之间暴力统计,既然是要求区间的最大最小值就可以用单调队列啦。还有一个很不错的做法可以进一步提高程序速度,拒绝暴力统计,在单调队列的基础上再开行的单调队列,就$N^2$了,但是感觉挺麻烦的就没写。
1 // luogu-judger-enable-o2 2 # include <cstdio> 3 # include <cstring> 4 # include <iostream> 5 # define R register int 6 # define inf 1000000009 7 8 using namespace std; 9 10 const int maxa=2005; 11 struct nod 12 { 13 int key,val; 14 }; 15 nod q1[maxa],q2[maxa]; 16 int a,b,n,h1,h2,t1,t2,big,sma; 17 int g[maxa][maxa]; 18 int minn[maxa][maxa],maxx[maxa][maxa]; 19 int ans=inf; 20 21 void ins1 (int i,int j) 22 { 23 int x=g[i+n-1][j+n-1]; 24 while (q1[h1].key<j&&h1<=t1) h1++; 25 while (q1[t1].val<=x&&h1<=t1) t1--; 26 q1[++t1].key=j+n-1; 27 q1[t1].val=x; 28 } 29 30 void ins2 (int i,int j) 31 { 32 int x=g[i+n-1][j+n-1]; 33 while (q2[h2].key<j&&h2<=t2) h2++; 34 while (q2[t2].val>=x&&h2<=t2) t2--; 35 q2[++t2].key=j+n-1; 36 q2[t2].val=x; 37 } 38 39 int main() 40 { 41 scanf("%d%d%d",&a,&b,&n); 42 for (R i=1;i<=a;++i) 43 for (R j=1;j<=b;++j) 44 scanf("%d",&g[i][j]); 45 for (R i=1;i<=a;++i) 46 for (R j=1;j<=b;++j) 47 minn[i][j]=inf; 48 for (R i=2-n;i<=a-n+1;++i) 49 { 50 memset(q1,0,sizeof(q1)); 51 h1=h2=1; 52 memset(q2,0,sizeof(q2)); 53 t1=t2=0; 54 for (R j=2-n;j<=b-n+1;++j) 55 { 56 ins1(i,j); 57 ins2(i,j); 58 if(j<1) continue; 59 maxx[i+n-1][j]=q1[h1].val; 60 minn[i+n-1][j]=q2[h2].val; 61 if(i<1) continue; 62 big=0,sma=inf; 63 for (int k=1;k<=n;++k) 64 big=max(big,maxx[i+k-1][j]),sma=min(sma,minn[i+k-1][j]); 65 ans=min(ans,big-sma); 66 } 67 } 68 printf("%d",ans); 69 return 0; 70 }
--shzr