DP练习2-0313(VJ)


比赛地址

T1(Brackets)

区间DP

先枚举断点,如果 \(l\)\(r\) 括号匹配,那就和 \(f[l+1][r-1]\)\(max\)

${\color{skyblue}{Code}}$
#include
#include
#include
#include
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

T2(Traveling by Stagecoach)

状压DP

压缩车票的状态。令 \(f[k][i]\) 表示从起点到第 \(i\) 个点时,车票状态为 \(k\) 时需要的最小时间。

每个点去找与它相连的点来更新当前的点。如果当前状态下 \(q\) 这张车票被用了,那就可以使其作为这两个点中使用的。

${\color{skyblue}{Code}}$
#include
#include
#include
#include
#include
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<>q)&1)
						f[i][k]=min(f[i][k],f[j][k^(1<>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<

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
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>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<

T4(Coloring Brackets)

题解都是用区间DP做的,但是也用了DFS。

可以把括号序列转化为一颗树,一对括号中的每一对括号是它的儿子节点。

这样就可以用树形DP做了。\(f[x][i][j]\) 表示 \(x\) 这个点包含的区间以 \(i\) 这个颜色开始,\(j\) 这个颜色结尾的方案数。

中间的转移有点烦要挨个枚举情况。记得取余和开 \(longlong\)

${\color{skyblue}{Code}}$
#include
#include
#include
#include
#include
#include
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 son[705];

void dp(int x){
	
	int tmp[3][3]={0};
	for(int i=0;i>s;
	
	int f=0;stack x;
	for(int i=0;i