DP练习2-0313(VJ)
T1(Brackets)
区间DP
先枚举断点,如果 \(l\) 和 \(r\) 括号匹配,那就和 \(f[l+1][r-1]\) 取 \(max\) 。
${\color{skyblue}{Code}}$
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
int f[N][N];
int main(){
string s;
while(cin>>s && s!="end"){
memset(f,0,sizeof f);
int le=s.size();
for(int len=1;len<=le;++len){
for(int l=0;l<=le-len;++l){
int r=l+len-1;
if(l==r) continue;
for(int j=l;j<r;++j)
f[l][r]=max(f[l][j]+f[j+1][r],f[l][r]);
if((s[l]=='(' && s[r]==')') || (s[l]=='[' && s[r]==']'))
f[l][r]=max(f[l+1][r-1]+2,f[l][r]);
}
}
cout<<f[0][le-1]<<endl;
}
return 0;
}
T2(Traveling by Stagecoach)
状压DP
压缩车票的状态。令 \(f[k][i]\) 表示从起点到第 \(i\) 个点时,车票状态为 \(k\) 时需要的最小时间。
每个点去找与它相连的点来更新当前的点。如果当前状态下 \(q\) 这张车票被用了,那就可以使其作为这两个点中使用的。
${\color{skyblue}{Code}}$
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N=(1<<8)+5,INF=0x3f3f3f3f;
double f[35][N];
int n,m,p,a,b;
int v[10],ed[35][35];
void dp(){
for(int i=1;i<=m;++i)
for(int j=0;j<(1<<n);++j) f[i][j]=INF;
for(int i=0;i<(1<<n);++i)
f[a][i]=0;
for(int k=0;k<(1<<n);++k)
for(int i=1;i<=m;++i){
if(i==a) continue;
for(int j=1;j<=m;++j){
if(ed[i][j]==-1) continue;
for(int q=0;q<n;++q)
if((k>>q)&1)
f[i][k]=min(f[i][k],f[j][k^(1<<q)]+1.0*ed[i][j]/v[q+1]);
}
}
}
int main(){
while(cin>>n>>m>>p>>a>>b && n){
memset(ed,-1,sizeof ed);
for(int i=1;i<=n;++i) cin>>v[i];
for(int i=1;i<=p;++i){
int u,v,z;
cin>>u>>v>>z;
ed[u][v]=ed[v][u]=z;
}
dp();
double ans=INF;
for(int i=0;i<(1<<n);++i) ans=min(ans,f[b][i]);
if(ans==INF) cout<<"Impossible"<<endl;
else printf("%.3f\n",ans);
}
return 0;
}
T3(World Eater Brothers)
树形DP(参考这篇)
题意:一颗树但边有向,求让这颗树入度为 0 的点个数最多 2 个需要改变方向的边的数量。
令 \(f[x][i][j](j=0/1)\) 表示 \(x\) 这个点及其子树中有 \(i\) 个点入度为 0 时且 \(x\) 这个点入度是否为 0 (1:是,0:否)。
枚举每个子树 \(y\) ,再枚举已合并的和子树的入度为 0 的点的个数,同时枚举 \(x\) ,\(y\) 入度分别是否为 0。
${\color{skyblue}{Code}}$
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N=3e3+5;
int n;
int h[N],val[N*2],ver[N*2],nxt[N*2],co;
int f[N][5][2];
void add(int x,int y,int z){
val[++co]=z,ver[co]=y,nxt[co]=h[x],h[x]=co;
}
void dp(int x,int fa){
f[x][1][1]=0;
for(int i=h[x];i;i=nxt[i]){
int y=ver[i],v=val[i];
if(y==fa) continue;
dp(y,x);
int g[5][2];memset(g,INF,sizeof g);
for(int j1=0;j1<3;++j1)
for(int j2=0;j2<3;++j2)
for(int kx=0;kx<2;++kx)
for(int ky=0;ky<2;++ky){
g[j1+j2][kx]=min(g[j1+j2][kx],f[y][j2+ky][ky]+f[x][j1][kx]+v);
\\x-->y
g[j1+j2][0]=min(g[j1+j2][0],f[y][j2][ky]+f[x][j1+kx][kx]+(v^1));
\\y-->x
}
memcpy(f[x],g,sizeof g);
}
}
int main(){
cin>>n;
for(int i=1;i<n;++i){
int x,y;cin>>x>>y;
add(x,y,0),add(y,x,1);
}
memset(f,INF,sizeof f);
dp(1,0);
int ans=INF;
for(int i=0;i<3;++i)
ans=min(ans,min(f[1][i][0],f[1][i][1]));
cout<<ans;
return 0;
}
T4(Coloring Brackets)
题解都是用区间DP做的,但是也用了DFS。
可以把括号序列转化为一颗树,一对括号中的每一对括号是它的儿子节点。
这样就可以用树形DP做了。\(f[x][i][j]\) 表示 \(x\) 这个点包含的区间以 \(i\) 这个颜色开始,\(j\) 这个颜色结尾的方案数。
中间的转移有点烦要挨个枚举情况。记得取余和开 \(longlong\) 。
${\color{skyblue}{Code}}$
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define int long long
const int p=1e9+7;
int b[705],l[705],r[705],fa[705];
int cnt;
int ff[705][3][3];
vector<int> son[705];
void dp(int x){
int tmp[3][3]={0};
for(int i=0;i<son[x].size();++i){
int y=son[x][i];
dp(y);
if(i==0)
for(int j=1;j<3;++j)
tmp[0][j]=ff[y][0][j],tmp[j][0]=ff[y][j][0];
else{
int tt[3][3]={0};
for(int i=0;i<3;++i){
for(int j=0;j<3;++j){
if(j==0){
tt[i][0]+=tmp[i][j]*(ff[y][1][0]+ff[y][2][0])%p,tt[i][0]%=p;
tt[i][1]+=tmp[i][j]*ff[y][0][1]%p,tt[i][1]%=p;
tt[i][2]+=tmp[i][j]*ff[y][0][2]%p,tt[i][2]%=p;
}
if(j==1){
tt[i][0]+=tmp[i][j]*ff[y][2][0]%p,tt[i][0]%=p;
tt[i][1]+=tmp[i][j]*ff[y][0][1]%p,tt[i][1]%=p;
tt[i][2]+=tmp[i][j]*ff[y][0][2]%p,tt[i][2]%=p;
}
if(j==2){
tt[i][0]+=tmp[i][j]*ff[y][1][0]%p,tt[i][0]%=p;
tt[i][1]+=tmp[i][j]*ff[y][0][1]%p,tt[i][1]%=p;
tt[i][2]+=tmp[i][j]*ff[y][0][2]%p,tt[i][2]%=p;
}
}
}
for(int i=0;i<3;++i)
for(int j=0;j<3;++j) tmp[i][j]=tt[i][j];
}
}
if(!son[x].size())
for(int i=1;i<3;++i) ff[x][0][i]=ff[x][i][0]=1;
else if(x)
for(int i=1;i<3;++i)
for(int j=0;j<3;++j)
for(int q=0;q<3;++q){
if(i!=j) ff[x][i][0]+=tmp[j][q],ff[x][i][0]%=p;
if(i!=q) ff[x][0][i]+=tmp[j][q],ff[x][0][i]%=p;
}
else
for(int i=0;i<3;++i)
for(int j=0;j<3;++j) ff[x][i][j]=tmp[i][j];
}
signed main(){
string s;
cin>>s;
int f=0;stack<int> x;
for(int i=0;i<s.size();++i){
if(s[i]=='('){
b[i]=++cnt,fa[cnt]=f,l[cnt]=i;
f=cnt;
x.push(i);
}
else{
int xx=x.top();x.pop();
b[i]=b[xx];r[b[i]]=i;
f=fa[b[i]];
}
}
for(int i=1;i<=cnt;++i) son[fa[i]].push_back(i);
dp(0);
int ans=0;
for(int i=0;i<3;++i)
for(int j=0;j<3;++j) ans=(ans+ff[0][i][j])%p;
cout<<ans;
return 0;
}