各种动态规划&贪心的知识
Update on 2019.3.21
1.单调栈求最大子矩形面积(以由“1”构成为例)
模板题:玉蟾宫 的代码
先预处理行/列上最长的连续$1$,然后维护一个行/列的单调栈
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=2005; 6 struct a 7 { 8 int xx,yy; 9 }stk[N],str; 10 int mapp[N][N],dp[N][N]; 11 int n,m,top,ans; 12 char rd[2]; 13 int main () 14 { 15 scanf("%d%d",&n,&m); 16 for(int i=1;i<=n;i++) 17 for(int j=1;j<=m;j++) 18 scanf("%s",rd),mapp[i][j]=(rd[0]=='F'); 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=m;j++) 21 if(mapp[i][j]) dp[i][j]=dp[i-1][j]+1; 22 for(int i=1;i<=n;i++) 23 for(int j=1;j<=m+1;j++) 24 { 25 str.xx=j,str.yy=dp[i][j]; 26 if(!top||stk[top].yy<str.yy) stk[++top]=str; 27 else 28 { 29 int tmp=j; 30 while(top&&stk[top].yy>=str.yy) 31 { 32 int tep=(j-stk[top].xx)*stk[top].yy; 33 tmp=stk[top--].xx,ans=max(ans,tep); 34 } 35 str.xx=tmp,stk[++top]=str; 36 } 37 } 38 printf("%d",3*ans); 39 return 0; 40 }
带权的话(有坏点)就维护前缀和再记录一下每个矩形的四个角
str.xx=j,str.yy=dp[i][j],str.zz=i; ...... check(stk[top].zz-stk[top].yy+1,stk[top].xx,stk[top].zz,j-1)
什么?带权没坏点?
$O(n^4)$啊!
2.$Johnson$法则
可以概括为“双机器流水线问题”:有$n$个物品要由两台机器加工,每个物品都先在第一台机器上加工完成后再由第二台机器加工。第$i$个物品在两台机器上加工所需时间分别为$t1_i$和$t2_i$,求最优方案
设两项任务分别为$x,y$,以 $min(t1_x,t2_y)<min(t2_x,t1_y)$为基准排序后即是最优方案
3.四边形不等式
若有一$dp$方程型如$dp(i,j)=min(dp(i,j),dp(i,k)+dp(k+1,j)+v(i,j))$,且对于任意$a,b,c,d$,都有$v(a,d)+v(b,c)>=v(a,c)+v(b,d)$(即包含和大于交叉和),同时区间包含关系也单调(即大区间代价永远比小区间大)。那么设一个区间的$(i,j)$的最优决策点为$g(i,j)$(初始$g[i][i]=i$),则区间$(i,j)$在$(g(i,j-1),g(i+1,j))$之间必可取得最优解,依此可将复杂度由$O(n^3)$降至$O(n^2)$,常用于区间$dp$
考场不会证明怎么办?打一张决策点$g$的表,也就是一个上三角矩阵,观察是否满足$g[i][j-1]<=g[i][j]<=g[i+1][j]$即可(即每行每列单调不降)
4.插头DP
考场直接$O(4^n)$暴力(雾
插头:轮廓线两侧格子的连接处,类似 清华集训2017无限之环 的水管(不要在意题目类型),然后因为种类不会很多,就逐个种类讨论然后转移。转移是一格一格的,同时边转移边
更新下一行的状态
存状态:哈希表,具体怎么存的话可以哈希/三进制(括号序列,左右空)/四进制(适应于计算机本身)。(引用@cyendra的博客)对于涉及到连通性的问题,code数组储存的是插头的连通
性;对于不涉及连通性的问题,code数组储存插头的有无。
对于涉及到连通性的问题,code数组储存的是插头的连通性。
对于不涉及连通性的问题,code数组储存插头的有无。
(甚至还没写过)
5.斯坦纳树
$dp[i][s]$表示以$i$为根$s$状态时的最小斯坦纳树,定出一个点做根之后分换根不换根转移,前者直接转移,后者边SPFA边转移。复杂度$O(n2^n)$
斯坦纳树板子题:WC2008 游览计划
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=11,inf=0x3f3f3f3f; 6 const int mov[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; 7 struct a 8 { 9 int x,y,s; 10 }lst[1<<N][N][N]; 11 struct c 12 { 13 int x,y; 14 }que[N*N*N]; 15 int n,m,f,b,sx,sy,cnt,all; 16 int mapp[N][N],dp[1<<N][N][N],inq[N][N],outp[N][N]; 17 void Bellman_Ford(int sta) 18 { 19 while(f<=b) 20 { 21 c tn=que[f++]; int tx=tn.x,ty=tn.y; 22 inq[tx][ty]=false; 23 for(int i=0;i<4;i++) 24 { 25 int nx=tx+mov[i][0],ny=ty+mov[i][1]; 26 if(nx>=1&&nx<=n&&ny>=1&&ny<=m) 27 { 28 int cst=dp[sta][tx][ty]+mapp[nx][ny]; 29 if(dp[sta][nx][ny]>cst) 30 { 31 dp[sta][nx][ny]=cst; 32 if(!inq[nx][ny]) 33 que[++b]=(c){nx,ny},inq[nx][ny]=true; 34 lst[sta][nx][ny]=(a){tx,ty,sta}; 35 } 36 } 37 } 38 } 39 } 40 void Getans(int sta,int xx,int yy) 41 { 42 if(!lst[sta][xx][yy].s) return ; 43 outp[xx][yy]=1; a tmp=lst[sta][xx][yy]; 44 Getans(tmp.s,tmp.x,tmp.y); 45 if(xx==tmp.x&&yy==tmp.y) 46 Getans(sta^tmp.s,xx,yy); 47 } 48 int main() 49 { 50 register int i,j,k,h; 51 scanf("%d%d",&n,&m); 52 memset(dp,0x3f,sizeof dp); 53 for(i=1;i<=n;i++) 54 for(j=1;j<=m;j++) 55 { 56 scanf("%d",&mapp[i][j]); 57 if(!mapp[i][j]) 58 { 59 dp[1<<cnt][i][j]=0; 60 sx=i,sy=j,cnt++; 61 } 62 } 63 all=(1<<cnt)-1; 64 for(i=1;i<=all;i++) 65 { 66 f=0,b=-1; 67 for(j=1;j<=n;j++) 68 for(k=1;k<=m;k++) 69 { 70 for(h=i;h;h=i&(h-1)) 71 { 72 int cst=dp[h][j][k]+dp[i^h][j][k]-mapp[j][k]; 73 if(dp[i][j][k]>cst) 74 lst[i][j][k]=(a){j,k,h},dp[i][j][k]=cst; 75 } 76 if(dp[i][j][k]!=inf) 77 que[++b]=(c){j,k},inq[j][k]=true; 78 } 79 Bellman_Ford(i); 80 } 81 printf("%d\n",dp[all][sx][sy]),Getans(all,sx,sy); 82 for(int i=1;i<=n;i++,puts("")) 83 for(int j=1;j<=m;j++) 84 if(mapp[i][j]) 85 outp[i][j]?printf("o"):printf("_"); 86 else 87 printf("x"); 88 return 0; 89 }
6.不知道是啥的贪心汇总(其实是直接粘的讲课时做的笔记(逃 )
求树上两条不相交路径的最大长度和
抵消的思想:求直径之后把直径的边权取反再求直径
最多k段最大子段和
类似上面的做法,取一次之后把取过的取反,$O(nk)$。用线段树优化成$O(k\log n)$
工作安排
经典的一种堆贪心,往后扫的同时维护一个合法的堆
最小乘积生成树
给有两种边权$(a,b)$的无向图,求一棵生成树使得$\sum a*\sum b$最小
把每一棵生成树抽象成$(\sum a,\sum b)$的点,然后递归搞 下凸壳
具体怎么搞这个博客写的很好
GCD guessing game
发现实际上是在分素数,于是变成 NOIP2007纪念品分组
7.拟阵
定义于一个有限集合$S$上的一个集合,是$S$的子集的集合
一些性质:
非空性:字面意思(拟阵自己不是空集)
遗传性:拟阵的非空子集也是拟阵
交换性:对于两个拟阵$A,B(|A|<|B|)$,在从$B$里往$A$里不断丢东西时,$A$将一直是个拟阵
线性拟阵
定义在一个矩阵的所有列向量的集上,令任意列向量$V∈M$当且仅当其中的列向量线性独立
8.DP不能出环
解决方法:记一个临时的变量最后赋给这个位置的$dp$数组(洛谷4037),或者用“第二优先级”的值来更新(BZOJ3743)
9.笛卡尔树
(无家可归的数据结构知识)
下标的BST+权值的堆,可以用来$O(n)$建Treap或者结合DP运用
构造使用单调栈维护笛卡尔树的链:
1 for(int i=1;i<=n;i++) 2 { 3 while(top&&a[stk[top]]>a[i]) 4 { 5 int nde=stk[top]; top--; 6 if(top&&a[stk[top]]>a[i]) 7 son[stk[top]][1]=nde,fth[nde]=stk[top]; 8 else 9 son[i][0]=nde,fth[nde]=i; 10 } 11 stk[++top]=i; 12 } 13 while(top>1) 14 { 15 son[stk[top-1]][1]=stk[top]; 16 fth[stk[top]]=stk[top-1],top--; 17 }

浙公网安备 33010602011771号