2022/3/13 DP专场

在开始之前,请允许我再吐槽一遍Vjudge的垃圾C++编译……

A.Brackets

  • 一道简单的区间DP(奈何我打了一个小时的线性DP才看出来)
  • \(f_{s,e}\) 表示在区间 \([s,e]\) 内最长的合格字串长度,枚举区间长度和区间起点,如果首尾两个字符是一对就特别判断一下,不然就枚举断点来合并区间;
AC code
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ios>
#include<sstream>
using namespace std;

string s;
int f[105][105];

char g(char x){
	if(x=='(') return ')';
	if(x=='[') return ']';
	if(x==')') return '(';
	return '[';
}

int main(){
//	freopen("1.txt","r",stdin);
	while(1){
		cin>>s;
		if(s=="end") return 0;
		memset(f,0,sizeof(f));
		int ans=0;
		for(int len=2;len<=s.size();len+=1){
			for(int st=0;st+len-1<s.size();++st){
				int e=st+len-1;
				if(s[st]==g(s[e]) && (s[st]=='(' || s[st]=='['))
					f[st][e]=max(f[st][e],f[st+1][e-1]+2);
				for(int k=st;k<e;++k){
					f[st][e]=max(f[st][e],f[st][k]+f[k+1][e]);
				}
			//	printf("%d %d %d\n",st+1,e+1,f[st][e]);
				ans=max(ans,f[st][e]);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

B.Traveling by Stargecoach

  • 一道美妙的状压DP
  • 观察数据范围可得,我们应该状压车票。因此我们用 \(0/1\) 来表示车票是否使用;
    \(f_{i,j}\) 表示在车票使用状态为 \(j\) 的情况下,到达 \(i\) 城市的最小花费;
    • 第一重循环枚举现在车票的使用状态;
    • 第二重循环枚举现在的状态可能由哪些状态变化得出;由于我们已知每一次至多使用一张车票,所以枚举这一位的车票有没有使用,如果使用就减去它得到上一个状态;
    • 第三重循环枚举在上一个状态中能够到达哪些点;
    • 第四重循环枚举这一次可能拓展出哪些点;
  • 最后枚举 \(f_{b,j}\) 中找最小值即可;
AC code
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ios>
#include<sstream>
using namespace std;

const int Inf=0x3f3f3f3f;

int n,m,p,a,b;
int t[10];
int maze[305][305];
double f[305][1<<9],ans;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

bool vis[305];
int ex[1<<9];

int main(){
//	freopen("2686.txt","r",stdin);
//	freopen("ans.txt","w",stdout);
//	prepare();
	while(1){
		n=read(),m=read(),p=read(),a=read(),b=read();
		if(!n && !m) return 0;
		memset(maze,0x3f,sizeof(maze));
		for(int i=0;i<n;++i)
			t[i]=read();
		int x,y,z;
		for(int i=1;i<=p;++i){
			x=read(),y=read(),z=read();
			maze[x][y]=maze[y][x]=min(maze[x][y],z);
		}
		ans=INT_MAX;
		memset(f,127,sizeof(f));
//		printf("%.3f\n",f[0][0]);
		f[a][0]=0;
		for(int i=0;i<1<<n;++i){
			for(int j=0;j<n;++j){
				if(!(i>>j)&1) continue;
				for(int k=1;k<=m;++k){
					if(f[k][i^(1<<j)]<Inf){
						for(int q=1;q<=m;++q){
							if(maze[k][q]<105){
								f[q][i]=min(f[q][i],f[k][i^(1<<j)]+1.0*maze[k][q]/t[j]);
							}
						} 
					}
				}
			}
		}
		for(int i=0;i<1<<n;++i)
			ans=min(ans,f[b][i]);
		(ans<INT_MAX)?printf("%.3f\n",ans):puts("Impossible");
	}
	return 0;
}

D.Coloring Brackets

  • 又是一道区间DP
  • 由于不仅要考虑相邻括号颜色不同,还要考虑一对括号有且仅有一个染色,因此在普通区间DP的基础上新增两个维度,\(f_{l,r,i,j}\) 表示区间 \([l,r]\) 中 当 \(l\)\(i\) 颜色,\(r\)\(j\) 颜色时的方案数(0为无色,1/2分别表示红/蓝);
  • 首先预处理出每一个括号分别匹配的另一个括号;
  • 状态转移方程如下:
    • \(l+1=r\) ,也就是最内层的括号时:
      \(f_{l,r,1,0}=f_{l,r,0,1}=f_{l,r,2,0}=f_{l,r,0,2}=1\);
    • \(r=pair[l]\),也就是最外层是一对括号时:
      先处理 \(dp(l+1,r-1)\);
      \(f_{l,r,1,0}+=f_{l+1,r-1,x,y}(x\not =1)\);
      \(f_{l,r,2,0}+=f_{l+1,r-1,x,y}(x\not =2)\);
      \(f_{l,r,0,1}+=f_{l+1,r-1,x,y}(y\not =1)\);
      \(f_{l,r,0,2}+=f_{l+1,r-1,x,y}(y\not =2)\);
    • \(r\not =pair[l]\),也就是最外层不是一对括号时:
      \(pair[l]\) 作为分界点将整个区间划分为两个部分:\(dp(l,pair[l]),dp(pair[l]+1,r)\)
      这两个部分之间是乘法的关系:
      \(f_{l,r,i,j}+=f_{l,pair[l],i,a}×f_{pair[l]+1,r,b,j}(a\not =b=0)\);
AC code
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ios>
#include<sstream>
#include<stack>
using namespace std;

const int mod=1e9+7;

long long f[705][705][3][3];
int pr[705];
string s;

void dp(int l,int r){
//	printf("%d %d\n",l,r);
	if(l+1==r){
		f[l][r][1][0]=f[l][r][0][1]=1;
		f[l][r][2][0]=f[l][r][0][2]=1;
		return ;
	}
	if(r==pr[l]){
		dp(l+1,r-1);
		for(int i=0;i<3;++i)
			for(int j=0;j<3;++j){
				if(i!=1) (f[l][r][1][0]+=f[l+1][r-1][i][j])%=mod;
				if(i!=2) (f[l][r][2][0]+=f[l+1][r-1][i][j])%=mod;
				if(j!=1) (f[l][r][0][1]+=f[l+1][r-1][i][j])%=mod;
				if(j!=2) (f[l][r][0][2]+=f[l+1][r-1][i][j])%=mod;
			}
	}
	else{
		dp(l,pr[l]);
		dp(pr[l]+1,r);
		for(int a=0;a<3;++a)
		for(int b=0;b<3;++b)
		for(int c=0;c<3;++c)
		for(int d=0;d<3;++d)
		if(c!=d || (!c && !d)) 
		(f[l][r][a][b]+=(1ll*f[l][pr[l]][a][c]*f[pr[l]+1][r][d][b]%mod))%=mod;
	}
	return ;
}

int main(){
	cin>>s;
	stack<int>st;
	for(int i=0;i<s.size();++i){
		if(s[i]=='(')
			st.push(i);
		else{
			int x=st.top();
			st.pop();
			pr[x]=i,pr[i]=x;
		}
	}
	dp(0,s.size()-1);
	long long ans=0;
	for(int i=0;i<3;++i)
		for(int j=0;j<3;++j)
			(ans+=0ll+f[0][s.size()-1][i][j])%=mod;
	printf("%lld",ans);
	return 0;
}

如果你想要双倍经验:洛谷CF149D Coloring Brackets

posted @ 2022-03-14 16:54  Star_LIcsAy  阅读(50)  评论(0)    收藏  举报