Living-Dream 系列笔记 第50期
T1
构思分讨。
很自然地,我们令 \(dp_{i,j}\) 表示 \([i,j]\) 的初始字母方案数。
但是这个状态信息过少,不足以解决此问题。
于是我们增加状态维度,令 \(dp_{i,j,0/1/2/3}\) 表示 \([i,j]\) 是否能由 W/I/N/D 演变而来。
答案即为 \(dp_{1,n,0} \mid \mid dp_{1,n,1} \mid \mid dp_{1,n,2} \mid \mid dp_{1,n,3}\)。
显然有初始状态 \(dp_{i,j,0/1/2/3} = 1\),因为无论哪个字母,它本身即可演变为它。其余为 \(\infty\)。
关于转移,我们考虑枚举中转点的套路去做。
对于区间 \([i,j]\),我们确定中转点 \(k\) 后,仅需枚举 \(c1,c2,c\),并检查 \(dp_{i,k,c1}=dp_{k+1,j,c2}=dp_{i,j,c}=1\),且 \(c1,c2\) 能演变为 \(c\) 即可。
code
#include<bits/stdc++.h> using namespace std; const int N=2e2+5; string t; int n,a[4]; int num[N],ok[4][4][4]; int dp[N][N][4]; int main(){ for(int i=0;i<4;i++) cin>>a[i]; num['W']=0,num['I']=1,num['N']=2,num['G']=3; for(int i=0;i<4;i++){ for(int j=1;j<=a[i];j++){ char c1,c2; cin>>c1>>c2; ok[i][num[c1]][num[c2]]=1; } } cin>>t,n=t.size(),t='#'+t; for(int i=1;i<=n;i++) dp[i][i][num[t[i]]]=1; for(int i=1;i<=n;i++){ for(int j=1;j+i-1<=n;j++){ int s=j,e=i+j-1; for(int k=s;k<e;k++) for(int c=0;c<4;c++) for(int c1=0;c1<4;c1++) for(int c2=0;c2<4;c2++) dp[s][e][c]|=(dp[s][k][c1]&&dp[k+1][e][c2]&&ok[c][c1][c2]); } } bool f=0; if(dp[1][n][0]) cout<<'W',f=1; if(dp[1][n][1]) cout<<'I',f=1; if(dp[1][n][2]) cout<<'N',f=1; if(dp[1][n][3]) cout<<'G',f=1; if(!f) cout<<"The name is wrong!"; return 0; }
T2
构思分讨 \(\times \ 2\)。
令 \(dp_{i,j}\) 表示关掉 \([i,j]\) 内所有灯的最少功率。
但是,我们在关 \([i,j]\) 时,最后停在那个点将会影响到答案。
进一步分析,我们发现,关 \([i,j]\) 时,有且仅有可能停在 \(i,j\) 两点上。
这是因为,我们如果停在了满足 \(i<p<j\) 的点 \(p\),则我们会走一遍 \(i \to j \to p\) 或 \(j \to i \to p\),这一定不会比只走 \(i \to j\) 或 \(j \to i\) 更优。
于是我们令 \(dp_{i,j,0/1}\) 表示关掉 \([i,j]\) 且停在 \(i/j\) 的最小功率。
答案即为 \(\min(dp_{1,n,0},dp_{1,n,1})\)。
关于初始状态,因为每次出发时总会将 \(c\) 关掉,
因此有初始状态 \(dp_{i,i,0/1}=\lvert x_i-x_c \rvert + sum_n-w_c\),其余为 \(\infty\)。
其中 \(x_i\) 表示位置,\(w_i\) 表示功率,\(sum_i\) 表示 \(w_i\) 的前缀和。
接下来考虑转移。
这题肯定不能用枚举中转点套路,因为无法确定终止位置。
于是用端点转移套路,具体地:
-
对于 \(dp_{i,j,0}\),可以 \(i+1 \to j \to i\) 或 \(j \to i+1 \to i\)。
因此有转移
-
对于 \(dp_{i,j,1}\),可以 \(i \to j-1 \to j\) 或 \(j-1 \to i \to j\)。
因此有转移
直接做就完了。
code
#include<bits/stdc++.h> using namespace std; int n,c; int x[53],w[53]; int sum[53]; int dp[53][53][2]; int main(){ cin>>n>>c; for(int i=1;i<=n;i++) cin>>x[i]>>w[i],sum[i]=sum[i-1]+w[i]; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][i][0]=dp[i][i][1]=abs(x[i]-x[c])*(sum[n]-w[c]); for(int i=2;i<=n;i++){ for(int j=1;j+i-1<=n;j++){ int s=j,e=j+i-1; dp[s][e][0]=min(dp[s+1][e][0]+(x[s+1]-x[s])*(sum[n]-sum[e]+sum[s]),dp[s+1][e][1]+(x[e]-x[s])*(sum[n]-sum[e]+sum[s])); dp[s][e][1]=min(dp[s][e-1][0]+(x[e]-x[s])*(sum[n]-sum[e-1]+sum[s-1]),dp[s][e-1][1]+(x[e]-x[e-1])*(sum[n]-sum[e-1]+sum[s-1])); } } cout<<min(dp[1][n][0],dp[1][n][1]); return 0; }
作业 T1
见 TJ。
作业 T2
构思分讨 \(\times \ 3\)。
法一:
令 \(dp_{i,j,0/1}\) 表示将 \([i,j]\) 变为颜色均为 \(c_i/c_j\) 的连通块的最小步数。
答案即为 \(\min(dp_{1,n,0},dp_{1,n,1})\)。
显然有初始状态 \(dp_{i,i,0/1}\)=0,其余为 \(\infty\)。
从状态易知,此题应用端点转移法进行转移。
于是有转移:
法一 code
#include<bits/stdc++.h> using namespace std; const int N=5e3+5; int n,c[N]; int dp[N][N][2]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>c[i]; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][i][0]=dp[i][i][1]=0; for(int i=2;i<=n;i++){ for(int j=1;j+i-1<=n;j++){ int s=j,e=j+i-1; dp[s][e][0]=min(dp[s][e][0],dp[s+1][e][0]+(c[s]!=c[s+1])); dp[s][e][0]=min(dp[s][e][0],dp[s+1][e][1]+(c[s]!=c[e])); dp[s][e][1]=min(dp[s][e][1],dp[s][e-1][0]+(c[e]!=c[s])); dp[s][e][1]=min(dp[s][e][1],dp[s][e-1][1]+(c[e]!=c[e-1])); } } cout<<min(dp[1][n][0],dp[1][n][1]); return 0; }
法二:
先预处理,将所有相同颜色全部合并到一起,组成一个色块。
接着令 \(dp_{i,j}\) 表示将 \([i,j]\) 变为一个连通块的最小步数。
答案即为 \(dp_{1,tot}\),\(tot\) 表示色块个数。
显然有初始状态 \(dp_{i,i}\)=0,其余为 \(\infty\)。
仍然采用端点转移法进行转移。
于是有转移:
其中 \(cc_i\) 表示第 \(i\) 个色块的颜色。
法二 code
#include<bits/stdc++.h> using namespace std; const int N=5e3+5; int n,tot,pre; int c[N],cc[N]; int dp[N][N]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>c[i]; for(int i=1;i<=n;i++){ if(c[i]!=pre) cc[++tot]=c[i]; pre=c[i]; } memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=tot;i++) dp[i][i]=0; for(int i=2;i<=tot;i++){ for(int j=1;j+i-1<=tot;j++){ int s=j,e=j+i-1; if(cc[s]==cc[e]) dp[s][e]=min(dp[s][e],dp[s+1][e-1]+1); else dp[s][e]=min(dp[s][e-1]+1,dp[s+1][e]+1); } } cout<<dp[1][tot]; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】