各种动态规划&贪心的知识

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 }
View Code

带权的话(有坏点)就维护前缀和再记录一下每个矩形的四个角

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 }
View Code

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     }
View Code

 

posted @ 2018-09-18 17:43  Speranza_Leaf  阅读(204)  评论(0)    收藏  举报