开始更新咯
DP专题【题目来源BZOJ】
一、树形DP
1.bzoj2286消耗战
题解:因为是树形结构,一个点与根节点不联通,删一条边即可,
于是我们就可以简化这棵树,把有用的信息建立一颗虚树,然后开始DP即可
1 /* 2 思路: 3 */ 4 #include<algorithm> 5 #include<cstdio> 6 #include<cmath> 7 #include<iostream> 8 #include<cstring> 9 #define ll long long 10 #define inf 1e60 11 #define N 250005 12 #define M 550005 13 using namespace std; 14 int n,m,k,tot,top,tmp,ind; 15 int a[N]; 16 int now[N],pre[M],v[M],val[M]; 17 int now1[N],pre1[M],v1[M]; 18 int fa[N][22],deep[N],dfn[N],bin[22]; 19 int z[M],st[M]; 20 ll f[N],mx[N]; 21 int read() 22 { 23 int x=0,f=1; char ch; 24 while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; 25 while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); 26 return x*f; 27 } 28 void ins(int a,int b,int c){ 29 ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c; 30 } 31 void insert(int a,int b){ 32 if (a==b) return; 33 ++tot; pre1[tot]=now1[a]; now1[a]=tot; v1[tot]=b; 34 } 35 void dfs(int x) 36 { 37 dfn[x]=++ind; 38 for (int i=1; bin[i]<=deep[x]; i++) 39 fa[x][i]=fa[fa[x][i-1]][i-1]; 40 for (int p=now[x]; p; p=pre[p]) 41 { 42 int son=v[p]; 43 if (son==fa[x][0]) continue; 44 deep[son]=deep[x]+1; 45 mx[son]=min(mx[x],(ll)val[p]); 46 fa[son][0]=x; 47 dfs(son); 48 } 49 } 50 int lca(int x,int y) 51 { 52 if(deep[x]<deep[y]) swap(x,y); 53 int t=deep[x]-deep[y]; 54 for (int i=0; bin[i]<=t; i++) 55 if (bin[i]&t) x=fa[x][i]; 56 for (int i=19; i>=0; i--) 57 if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; 58 if (x==y) return x; return fa[x][0]; 59 } 60 void prework() 61 { 62 bin[0]=1; 63 for (int i=1; i<=21; i++) bin[i]=bin[i-1]*2; 64 tmp=0; mx[1]=inf; dfs(1); 65 } 66 bool cmp(int a,int b){return dfn[a]<dfn[b]; 67 } 68 void work() 69 { 70 sort(a+1,a+k+1,cmp); 71 top=tot=0; 72 z[1]=a[1]; top++; 73 for (int i=2; i<=k; i++) if (lca(z[top],a[i])!=z[top]) z[++top]=a[i]; 74 st[1]=1; tmp=1; 75 for (int i=1; i<=top; i++) 76 { 77 int now=z[i],f=lca(now,st[tmp]); 78 for (;;) 79 { 80 if (deep[f]>=deep[st[tmp-1]]) 81 { 82 insert(f,st[tmp--]); 83 if (st[tmp]!=f) st[++tmp]=f; 84 break; 85 } 86 insert(st[tmp-1],st[tmp]); tmp--; 87 } 88 if (st[tmp]!=now) st[++tmp]=now; 89 } 90 while (--tmp) insert(st[tmp],st[tmp+1]); 91 } 92 void dp(int x) 93 { 94 f[x]=mx[x]; ll sum=0; 95 for (int p=now1[x]; p; p=pre1[p]) 96 { 97 int son=v1[p]; dp(son); sum+=f[son]; 98 } 99 now1[x]=0; 100 if (sum==0) f[x]=mx[x]; else f[x]=min(f[x],sum); 101 } 102 void init() 103 { 104 n=read(); 105 for (int i=1; i<n; i++) 106 { 107 int u=read(),v=read(),val=read(); 108 ins(u,v,val); ins(v,u,val); 109 } 110 // m=read(); 111 prework(); 112 } 113 void solve() 114 { 115 m=read(); 116 for (int i=1; i<=m; i++) 117 { 118 k=read(); for (int j=1; j<=k; j++) a[j]=read(); work(); 119 dp(1); printf("%lld ",f[1]); 120 } 121 } 122 int main() 123 { 124 init(); 125 solve(); 126 return 0; 127 }
2.bzoj1827奶牛大集会
题解:树形DP,先以1号点为根节点,然后再次树形DP求出其他节点为根的答案即可
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<cstdio> 6 #define ll long long 7 #define inf 1e60 8 #define N 200005 9 #define M 500005 10 using namespace std; 11 int n,m; 12 int now[N],pre[M],v[M],val[M],tot; 13 ll f[N]; 14 int size[N],num[N],dis[N]; 15 ll ans,sum; 16 void ins(int a,int b,int c) 17 { 18 ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c; 19 } 20 int read() 21 { 22 int x=0,f=1; char ch; 23 while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; 24 while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); 25 return x*f; 26 } 27 void init() 28 { 29 n=read(); 30 for (int i=1; i<=n; i++) num[i]=read(),sum+=num[i]; 31 for (int i=1; i<n; i++) 32 { 33 int u=read(),v=read(),val=read(); 34 ins(u,v,val); ins(v,u,val); 35 } 36 } 37 void dfs(int x,int fa) 38 { 39 size[x]=num[x]; 40 for (int p=now[x]; p; p=pre[p]) 41 { 42 int son=v[p]; 43 if (son==fa) continue; 44 dis[son]=dis[x]+val[p]; 45 f[son]=1ll*dis[son]*num[son]; 46 dfs(son,x); 47 size[x]+=size[son]; 48 f[x]+=f[son]; 49 } 50 } 51 void treedp(int x,int fa) 52 { 53 for (int p=now[x]; p; p=pre[p]) 54 { 55 int son=v[p]; 56 if (son==fa) continue; 57 f[son]=f[x]-1ll*size[son]*val[p]+1ll*(sum-size[son])*val[p]; 58 treedp(son,x); 59 } 60 } 61 void solve() 62 { 63 dfs(1,0); 64 //cout<<" "<<sum<<endl; 65 treedp(1,0); 66 ans=inf; 67 for (int i=1; i<=n; i++) ans=min(ans,f[i]); 68 printf("%lld ",ans); 69 } 70 int main() 71 { 72 init(); 73 solve(); 74 } 75 /* 76 77 */
3.bzoj1596电话网络
题解:树形DP,f[i][1]代表以i为根的子树都被覆盖的代价且在i放,[0]为都被覆盖i不放,
[2]为子树被覆盖,i没被覆盖的代价
f[i][1]=min(f[son][0],f[son][1],f[son][2])+1;
f[i][0]=最小的f[son][1]+min(f[son][0],f[son][1]);
f[x][2]+=f[son][0];
#include<algorithm> #include<iostream> #include<cstring> #include<cmath> #include<cstdio> #define ll long long #define inf 100000000 #define N 10005 #define M 30005 using namespace std; int n; int pre[M],v[M],now[N],tot; int f[N][3]; void ins(int a,int b){++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; } int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void dfs(int x,int fa) { int sum=0; f[x][1]=1; f[x][0]=inf; for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; dfs(son,x); f[x][1]+=min(f[son][0],min(f[son][1],f[son][2])); f[x][2]+=f[son][0]; } for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; int who=son; sum=f[son][1]; for (int j=now[x]; j; j=pre[j]) if ((v[j]!=fa) && (v[j]!=who)) sum+=min(f[v[j]][0],f[v[j]][1]); f[x][0]=min(f[x][0],sum); } } int main() { n=read(); for (int i=1; i<n; i++) { int u=read(),v=read(); ins(u,v); ins(v,u); } dfs(1,0); printf("%d ",min(f[1][0],f[1][1])); }
4.bzoj1864三色二叉树
题解:f[i][0]代表i为绿色的答案,f[i][1]为i为其他颜色的答案
f[i][0]=max(f[ls[i]][0]+f[rs[i]][1],f[ls[i]][1]+f[rs[i]][0]);
f[i][1]=f[ls[i]][0]+f[rs[i]][1]+1;
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<cstdio> #define ll long long #define inf 1000000000 #define N 300005 using namespace std; int ls[N],rs[N]; int n,m,size,ans1,ans2; int f[N][2]; void read(int x) { char ch=getchar(); if (ch=='0') return; if (ch=='1') ls[x]=++size,read(ls[x]); if (ch=='2') ls[x]=++size,read(ls[x]),rs[x]=++size,read(rs[x]); } void dp1(int x) { if (!x) return ; dp1(ls[x]); dp1(rs[x]); f[x][1]=f[ls[x]][0]+f[rs[x]][0]+1; f[x][0]=max(f[ls[x]][0]+f[rs[x]][1],f[ls[x]][1]+f[rs[x]][0]); } void dp2(int x) { if (!x) return; dp2(ls[x]); dp2(rs[x]); f[x][1]=f[ls[x]][0]+f[rs[x]][0]+1; f[x][0]=min(f[ls[x]][0]+f[rs[x]][1],f[ls[x]][1]+f[rs[x]][0]); } void solve() { dp1(1); ans1=max(f[1][0],f[1][1]); memset(f,0,sizeof(f)); dp2(1); ans2=min(f[1][0],f[1][1]); printf("%d %d ",ans1,ans2); } int main() { read(++size); solve(); }
5.bzoj1060时态同步
题解:f[i]代表以i为子树的儿子时间同步所需长度
ans+=maxsonf-(f[son]+val[p])
:val[p]为边权,maxsonf为i为子树的最长的长度
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 500005 #define M 1000005 using namespace std; int now[N],pre[M],v[M],val[M],f[N]; int n,m,s,tot; ll ans; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void ins(int a,int b,int c) { ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c; } void work(int x,int fa) { for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; work(son,x); f[x]=max(f[x],f[son]+val[p]); } for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; ans+=f[x]-(f[son]+val[p]); } } int main() { n=read(); s=read(); for (int i=1; i<n; i++) { int u=read(),v=read(),val=read(); ins(u,v,val); ins(v,u,val); } work(s,0); printf("%lld ",ans); }
6.bzoj2435道路修建
题解:ans+=val[p]*size[i]*(n-size[i]) size[i]为i为根的子树大小
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 1000005 #define M 2000005 using namespace std; int now[N],pre[M],v[M],val[M],size[N]; int n,tot; ll ans; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void ins(int a,int b,int c) { ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c; } void dfs(int x,int fa) { size[x]=1; for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; dfs(son,x); size[x]+=size[son]; ans+=1ll*val[p]*(abs(n-size[son]-size[son])); } } int main() { n=read(); for (int i=1; i<n; i++) { int u=read(),v=read(),val=read(); ins(u,v,val); ins(v,u,val); } dfs(1,0); printf("%lld ",ans); }
7.bzoj2427软件安装
题解:缩点+反边+dp【详见代码】
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 2000000000000 #define N 205 #define M 605 using namespace std; int n,m; int cost[N],val[N],depend[N],belong[N]; struct data{ int tot,pre[M],now[N],v[M]; }a,b; int tot,dfn[N],low[N],z[N],id; bool vis[N],in[N]; int sc[N],sv[N],type; int f[N][M]; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void ins1(int u,int v) { // cout<<" "<<u<<" "<<v<<endl; ++a.tot; a.pre[a.tot]=a.now[u]; a.now[u]=a.tot; a.v[a.tot]=v; } void ins2(int u,int v) { //cout<<" "<<u<<" "<<v<<endl; ++b.tot; b.pre[b.tot]=b.now[u]; b.now[u]=b.tot; b.v[b.tot]=v; } void tarjan(int x){ int son=0; dfn[x]=low[x]=++id; z[++tot]=x; vis[x]=true; for (int p=a.now[x]; p; p=a.pre[p]) { son=a.v[p]; if (!dfn[son]) { tarjan(son); low[x]=min(low[x],low[son]); } else if (vis[son]) low[x]=min(low[x],dfn[son]); } if (low[x]==dfn[x]) { ++type; while (son!=x) { son=z[tot--]; vis[son]=false; belong[son]=type; sv[type]+=val[son]; sc[type]+=cost[son]; } } } void init_pre() { n=read(); m=read(); for (int i=1; i<=n; i++) cost[i]=read(); for (int i=1; i<=n; i++) val[i]=read(); for (int i=1; i<=n; i++) { int x=read(); if(x)ins1(x,i); } for (int i=1; i<=n; i++) { if (!dfn[i]) tarjan(i); } for (int i=1; i<=n; i++) { for (int p=a.now[i]; p; p=a.pre[p]) { int son=a.v[p]; if (belong[son]!=belong[i]) ins2(belong[i],belong[son]),in[belong[son]]=true; } } for (int i=1; i<=type; i++) { if (!in[i]) ins2(type+1,i); } type++; } void dp(int x) { for (int p=b.now[x]; p; p=b.pre[p]) { int son=b.v[p]; dp(son); for (int j=m-sc[x]; j>=0; j--) for (int k=0; k<=j; k++) f[x][j]=max(f[x][j],f[x][j-k]+f[son][k]); } for (int j=m; j>=0; j--) { if (j>=sc[x]) f[x][j]=f[x][j-sc[x]]+sv[x]; else f[x][j]=0; } } void solve() { dp(type); printf("%d ",f[type][m]); } int main() { init_pre(); solve(); }
8.bzoj1369GEM
题解:开始智障了,以为只有整棵树为1或2的情况,于是GG了,实践证明数值个数不会超过5.
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 10005 #define M 20005 using namespace std; int n,m,tot; int pre[M],v[M],now[N]; int f[N][21]; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void ins(int a,int b) {++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; } void dp(int x,int fa) { for (int i=1; i<=5; i++) f[x][i]=i; for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; dp(son,x); } for (int i=1; i<=5; i++) { for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; int sum=inf; for (int j=1; j<=5; j++) { if (j==i) continue; sum=min(sum,f[son][j]); } f[x][i]+=sum; } } } void solve() { dp(1,0); int ans=inf; for (int i=1; i<=5; i++) ans=min(ans,f[1][i]); printf("%d ",ans); } int main() { n=read(); for (int i=1; i<n; i++) { int u=read(),v=read(); ins(u,v); ins(v,u); } solve(); }
9.bzoj3573米特运输
题解:可以发现一个特点,当我们确定一个点的时候,整棵树都可以确定,于是我们枚举留下留哪一个点且记录根的值,n-一样的 即为答案
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 500005 #define M 1000005 using namespace std; int n; int pre[M],v[M],now[N],a[N]; double s[N],val[N]; int in[N]; int tot,tmp,ans; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void ins(int a,int b) { ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; } void dp(int x,int fa) { for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; s[son]=s[x]+log(in[x]); dp(son,x); } } int main() { n=read(); for (int i=1; i<=n; i++) a[i]=read(); for (int i=1; i<n; i++) { int u=read(),v=read(); ins(u,v); ins(v,u); in[u]++; in[v]++; } for (int i=2; i<=n; i++) in[i]--; s[1]=log(1); dp(1,0); for (int i=1; i<=n; i++) { val[i]=s[i]+log(a[i]); } sort(val+1,val+n+1); ans=n*2; tmp=1; for (int i=2; i<=n; i++) { if (val[i]-val[i-1]<1e-5) tmp++; else tmp=1; ans=min(ans,n-tmp); } printf("%d ",ans); }
10.bzoj3522Hotel
题解:因为我没妹子所以就不屑于写这种题【虐狗啊】
枚举节点,以这个节点为中心,于是我们只要知道到这个节点的距离为J的有多少,然后组合排列的转换,搞一搞就好了
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 5005 #define M 10005 using namespace std; int pre[M],v[M],now[N]; int tmp[N],dis[N],s1[N],s2[N]; int tot,n,m,mx; ll ans; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void ins(int a,int b){++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; } void dp(int x,int fa) { tmp[dis[x]]++; mx=max(mx,dis[x]); for (int p=now[x]; p; p=pre[p]) { int son=v[p]; if (son==fa) continue; dis[son]=dis[x]+1; dp(son,x); } } int main() { n=read(); for (int i=1; i<n; i++) { int u=read(),v=read(); ins(u,v); ins(v,u); } ans=0; for (int i=1; i<=n; i++) { memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2)); for (int p=now[i]; p; p=pre[p]) { int son=v[p]; mx=dis[son]=1; dp(son,i); for (int j=1; j<=mx; j++) { ans+=s2[j]*tmp[j]; s2[j]+=tmp[j]*s1[j]; s1[j]+=tmp[j]; tmp[j]=0; } } } printf("%lld ",ans); }
小结:在树形DP过程中当我们不需要那些没有节点我们可以利用虚树来解决;
树形DP一般是在一棵树上的,且状态较多,贪心无法解决的问题;
树形DP一般可以看出,但是转移方程需要细想;
二、区间动规
1.bzoj1260涂色
题解:f[i][j]为i--j的涂色最小值
f[i][j]=f[i+1][j]+a[i+1]==a[i]
=f[i][j-1]+a[j-1]==a[j]
=f[i+1][j-1](a[i]==a[j]==a[i+1]==a[j-1])
=f[i+1][j-1]+(a[i]==a[j]) [ a[i+1]!=a[i]||a[j]!=a[j-1] ]
或者:a[i]==a[j]
则:f[i][j]=min(f[i+1][j],f[i][j-1])
f[i][j]=f[i+1][j-1]+1;
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 100 using namespace std; char s[N]; int f[N][N]; int n,ans; void dp() { for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) f[i][j]=inf/2; for (int i=1; i<=n; i++) f[i][i]=1; for (int len=1; len<n; len++) { for (int i=1; i<=n; i++) { int j=i+len; if (j>n) break; if (s[i]==s[j]) { if (len==1) f[i][j]=1; else { f[i][j]=min(f[i+1][j],f[i][j-1]); f[i][j]=min(f[i][j],f[i+1][j-1]+1); } } else { for (int k=i; k<j; k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]); } } } } int main() { scanf("%s",s+1); n=strlen(s+1); dp(); printf("%d ",f[1][n]); }
2.bzoj1090字符串压缩
题解:f[i][j]---->i到j被压缩的最小长度;
f[i][j]=min(f[i][k]+f[k+1][j]);
same(i,j)代表i到j可以被压缩,【同下题解释】
f[i][j]=i--j的最小段数的位数+2+i--j的最小段长;
注:他们可以被压缩,但是我们不一定要全部压缩
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> #define N 105 using namespace std; char s[N]; int n; bool mark[N][N]; int f[N][N]; bool same(int l,int r,int ll,int rr) { if ((r-l+1)%(rr-ll+1)!=0) return false ; int tmp=(rr-ll+1); for (int i=l; i<=r; i++) { int tmp=rr-ll+1; if (s[i]!=s[(i-l)%tmp+ll]) return false; } return true; } int get(int x) { int t=0; while (x) { x/=10; t++; } return t; } int dp(int l,int r) { int tmp=(r-l+1); if (tmp==1) return tmp; if (mark[l][r]) return f[l][r]; mark[l][r]=1; for (int i=l; i<r; i++) { tmp=min(tmp,dp(l,i)+dp(i+1,r)); if (same(i+1,r,l,i)) tmp=min(tmp,dp(l,i)+2+get((r-i)/(i-l+1)+1)); } return f[l][r]=tmp; } int main() { scanf("%s",s+1); n=strlen(s+1); printf("%d ",dp(1,n)); }
3.bzoj1068压缩
题解:f[i][j][t]---->i到j的这一段中间插了(T==1) M的最小压缩长度;
f[i][j][1]=min(f[i][k][1]+f[k+1][j][1]);
f[i][j][t]=min(f[i][k][t]+j-k);
same(i,j) f[i][j][t]=f[i][(i+j)/2][0]+1;
1 /* 2 f[i][j][t]---->i到j的这一段中间插了(T==1) M; 3 */ 4 #include<algorithm> 5 #include<cstdio> 6 #include<cstring> 7 #include<cmath> 8 #define N 105 9 using namespace std; 10 int n; 11 char s[N]; 12 int f[N][N][2]; 13 bool mark[N][N][2]; 14 bool same(int l,int r) 15 { 16 int tmp=r-l+1; 17 if (tmp&1) return false; 18 for (int i=l; i<=(l+r)/2; i++) 19 { 20 if (s[i]!=s[i+tmp/2]) return false; 21 } 22 return true; 23 } 24 int dp(int l,int r,int t) 25 { 26 int tmp=r-l+1; 27 if (tmp==1) return 1; 28 if (mark[l][r][t]) return f[l][r][t]; 29 mark[l][r][t]=1; 30 if (t==1) for (int i=l; i<r; i++) tmp=min(tmp,dp(l,i,1)+dp(i+1,r,1)+1); 31 for (int i=l; i<r; i++) tmp=min(tmp,dp(l,i,t)+r-i); 32 if (same(l,r)) tmp=min(tmp,dp(l,(l+r)/2,0)+1); 33 return f[l][r][t]=tmp; 34 } 35 int main() 36 { 37 scanf("%s",s+1); 38 n=strlen(s+1); 39 printf("%d ",dp(1,n,1)); 40 }
小结:区间DP一般处理区间覆盖、压缩之类问题,但是容易与数据结构弄混,
导致代码长度大;
三、状压
/*
序:因为状压dp方式类似,
故具体方程不予列出,
细节详见代码;
*/
1.bzoj1725牧场的安排
题解:f[i][j]代表第i行状态为j的情况数;
转移见代码;
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 13 #define M 4096 #define base 100000000 using namespace std; int f[N][M],val[N]; int maxn,n,m,ans; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void dp() { for (int i=0; i<=maxn; i++) if((i&(i>>1))==0&&(i|val[1])==val[1]) f[1][i]=1;//,cout<<" "<<i<<endl; // if (((i & (i>>1))==0) && (i|val[1])==val[1]) f[1][i]==1,cout<<" dd"<<endl; for (int i=2; i<=n; i++) { for (int j=0; j<=maxn; j++) { if (f[i-1][j]) for (int k=0; k<=maxn; k++) { //if (((k&(k>>1))==0) && (k|val[i])==val[i] && (k&j)==0) f[i][k]=(f[i][k]+f[i-1][j])%base,cout<<" dd "<<endl; if((j&k)==0&&(k|val[i])==val[i]&&(k&(k>>1))==0) f[i][k]=(f[i][k]+f[i-1][j])%base; } } } } int main() { n=read(); m=read(); for (int i=1; i<=n; i++) { for (int j=1; j<=m; j++) { int x=read(); val[i]<<=1; val[i]+=x; } } maxn=(1<<m)-1; dp(); for (int i=0; i<=maxn; i++) { ans=(ans+f[n][i])%base; } printf("%d ",ans); }
2.bzoj1087互不侵犯king
题解:同上,具体见代码;
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 10 #define M 525 #define KK 9*9+5 using namespace std; int n,K,maxn; ll ans; ll f[N][M][KK]; int t[N],num[M]; bool bo[M],can[M][M]; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } void prework() { t[1]=1; for (int i=2; i<=n; i++) t[i]=t[i-1]<<1; maxn=(1<<n)-1; for (int i=0; i<=maxn; i++) { if (i&(i>>1)) continue; int x=0; for (int j=1; j<=n; j++) if (i & t[j]) x++; num[i]=x; bo[i]=1; } for (int i=0; i<=maxn; i++) if (bo[i]) for (int j=0; j<=maxn; j++) if (bo[j]) { if ((i&j)==0 && ((i<<1) & j)==0 && ((j<<1) & i)==0) can[i][j]=1; } } void dp() { for (int i=0; i<=maxn; i++) if (bo[i]) f[1][i][num[i]]=1; for (int i=1; i<n; i++) for (int j=0; j<=maxn; j++) if (bo[j]) for (int k=0; k<=maxn; k++) if (bo[k]) if (can[j][k]) { for (int p=num[j]; p+num[k]<=K; p++) f[i+1][k][p+num[k]]+=f[i][j][p]; } } int main() { n=read(); K=read(); prework(); dp(); for (int i=0; i<=maxn; i++) ans+=f[n][i][K]; printf("%lld ",ans); return 0; }
3.bzoj1688疾病管理
题解:f[i]疾病状态为i的最大选取牛数
j|val[k]==i;f[i]=max(f[j|val[k]]+1);
注:val[k]为第k头牛的疾病状态;
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define M 42768 using namespace std; int n,d,k,maxn; int f[M],val[M]; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } bool check(int x) { int cnt=0; while (x) { cnt+=x&1; x>>=1; } return cnt<=k; } int main() { n=read(); d=read(); k=read(); for (int i=1; i<=n; i++) { int x=read(),a; while (x--) a=read(),val[i]|=(1<<(a-1)); } maxn=(1<<d)-1; for (int i=1; i<=n; i++) for (int j=maxn; j; j--) f[j|val[i]]=max(f[j|val[i]],f[j]+1); int ans=0; for (int i=0; i<=maxn; i++) if (check(i)) ans=max(ans,f[i]); printf("%d ",ans); } /* 6 3 2 0 1 1 1 2 1 3 2 2 1 2 2 1 */
4.bzoj2073PRZ
题解:f[i]为状态为i,本次选择重量为j的最小时间
sc[j]<M; f[i]=min(f[i],f[j^i]+sw[j]);
sc[j],sw[j]分别为状态为j的重量与时间
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 20 #define M 70000 using namespace std; int W,n,maxn; int f[M],st[M],sw[M]; int t[N],w[N]; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } /* 颓废死亡与坚持成功,请自己选择; */ void prework() { maxn=(1<<n)-1; for (int i=0; i<=maxn; i++) { int x=i,z=1; while (x) { if (x&1) sw[i]+=w[z],st[i]=max(st[i],t[z]); x>>=1; z++; } } } void dp() { f[0]=0; for (int i=1; i<=maxn; i++) { f[i]=inf/3; for (int j=i; j; j=i&(j-1)) if (sw[j]<=W) f[i]=min(f[i],f[i^j]+st[j]); } } int main() { W=read(); n=read(); for (int i=1; i<=n; i++) { t[i]=read(); w[i]=read(); } prework(); dp(); printf("%d ",f[maxn]); }
5.bzoj1231混乱的奶牛
题解:f[i][j]为状态为i,末尾奶牛为j的答案;
详见代码;
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff #define N 17 #define M 70000 using namespace std; int n,m,maxn; int s[N],bin[N]; ll f[M][N],ans; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } /* 颓废死亡与坚持成功,请自己选择; */ int main() { n=read(); m=read(); for (int i=1; i<=n; i++) s[i]=read(); bin[1]=1; for (int i=2; i<=n; i++) bin[i]=bin[i-1]<<1; for (int i=1; i<=n; i++) f[bin[i]][i]=1; maxn=(1<<n)-1; for (int i=1; i<=maxn; i++) { for (int j=1; j<=n; j++) if (i&bin[j]) for (int k=1; k<=n; k++) if ((bin[k]|i)!=i && abs(s[j]-s[k])>m) f[bin[k]|i][k]+=f[i][j]; } for (int i=1; i<=n; i++) ans+=f[maxn][i]; printf("%lld ",ans); }
6.bzoj1072排列
题解:f[i][j]状态为i 余数为 j的状态数
f[i|bin[k-1]][(j*10+a[k])%d]+=f[i][j]
最后排列组合去重;
#include<algorithm> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define ll long long #define inf 0x7ffffff using namespace std; int bin[20]; int Case,d,len,maxn; int a[15],v[15],tot[15],f[1025][1005]; char s[15]; int read() { int x=0,f=1; char ch; while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1; while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); return x*f; } /* 颓废死亡与坚持成功,请自己选择; */ int main() { Case=read(); bin[0]=1;for(int i=1;i<20;i++)bin[i]=bin[i-1]<<1; while (Case--) { scanf("%s",s+1); d=read(); len=strlen(s+1); for (int i=0; i<=9; i++) v[i]=1,tot[i]=0; for (int i=1; i<=len; i++) { a[i]=s[i]-'0'; tot[a[i]]++; v[a[i]]*=tot[a[i]]; } for (int i=0; i<bin[len]; i++) for (int j=0; j<d; j++) f[i][j]=0; f[0][0]=1; for(int i=0;i<bin[len];i++) for(int k=0;k<d;k++) if(f[i][k]) for(int x=1;x<=len;x++) if((bin[x-1]&i)==0) f[i|bin[x-1]][(a[x]+k*10)%d]+=f[i][k]; for (int i=0; i<=9; i++) f[bin[len]-1][0]/=v[i]; printf("%d ",f[bin[len]-1][0]); } }
小结:一般看到数据在20左右,就马上要想到状压DP;
四、斜率优化
1.bzoj1911特别行动队
题解:
待更------------------------