当前位置: 首页 > news >正文

CSP-S模拟37

T1:回文(string)

思路:

由于本题的数据范围较小,所以可能有多种 \(dp\) 状态,这里只呈现其中可能较典的两种外加一种暴搜最优解。

DP1:

我们设 \(f_{i,j,x,y}\) 表示使用 \(a\) 串的 \(i\) ~ \(j\)\(b\) 串的 \(x\) ~ \(y\) 能否拼成一个回文串。

首先考虑原始状态是什么样的。显然原始状态有三种大情况: \(a,b\) 中的单个字符,\(a,b\) 中相邻的两个相同的字符以及 \(a,b\) 串中相同的字符。这些显然都是初始能构成回文串的字符。

然后再考虑转移。显然有四种转移方式:\(a\) 串自己左右扩展, \(b\) 串自己左右扩展, \(a\) 串左端与 \(b\) 串右端匹配, \(a\) 串右端与 \(b\) 串左端匹配。

最后我们枚举每个串截取的长度,然后枚举起点,计算出终点。若 \(f_{i,j,x,y}\)\(1\) ,则 \(ans=max(ans,lena+lenb)\)

\(O(Tn^4)\) 的时间复杂度。跑得还是比较快的。

代码:

$code$
#include<iostream>
#include<cstring>
using namespace std;
int T,n,m,ans,f[55][55][55][55];
string a,b;
int main(){freopen("string.in","r",stdin);freopen("string.out","w",stdout);ios::sync_with_stdio(false);cin>>T;while(T--){ans=1;memset(f,0,sizeof(f));cin>>a>>b;a=' '+a;b=' '+b;n=a.size()-1;m=b.size()-1;for(int i=1;i<=n;i++) for(int j=1;j<=m+1;j++) f[i][i][j][j-1]=1;for(int j=1;j<=m;j++) for(int i=1;i<=n+1;i++) f[i][i-1][j][j]=1;for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i]==b[j]) f[i][i][j][j]=1;for(int i=1;i<n;i++){if(a[i]==a[i+1]){for(int j=1;j<=m+1;j++){ans=2;f[i][i+1][j][j-1]=1;}}}for(int j=1;j<m;j++){if(b[j]==b[j+1]){for(int i=1;i<=n+1;i++){ans=2;f[i][i-1][j][j+1]=1;}}}for(int lena=0;lena<=n;lena++){for(int i=1;i<=n-lena+1;i++){int j=i+lena-1;for(int lenb=0;lenb<=m;lenb++){for(int x=1;x<=m-lenb+1;x++){int y=x+lenb-1;if(f[i][j][x][y]){ans=max(ans,lena+lenb);if(i>1&&j<n&&a[i-1]==a[j+1]) f[i-1][j+1][x][y]=1;if(x>1&&y<m&&b[x-1]==b[y+1]) f[i][j][x-1][y+1]=1;if(i>1&&y<m&&a[i-1]==b[y+1]) f[i-1][j][x][y+1]=1;if(x>1&&j<n&&a[j+1]==b[x-1]) f[i][j+1][x-1][y]=1;}}}}}cout<<ans<<'\n';}return 0;
}//题解方法 (较快) 

DP2:

我们设 \(f_{i,j,x,y}\) 表示使用 \(a\) 串的 \(i\) ~ \(j\)\(b\) 串的 \(x\) ~ \(y\) 能拼成回文串的最长长度。

还是先考虑初始状态,显然跟上面的是一样的,不过上述的状态一初始值为 \(1\) ,状态二、三的初始值为 \(2\) (因为存的是长度嘛)

然后转移就是正常的转移了。不过记得特判一下把单个字符放到另一个串里的情况喔~~

最后取 \(max\) 就好啦~~

时间复杂度也是 \(O(Tn^4)\) 的,不过或许是因为大力分讨,跑起来不如上面那个快。

$code$
#include<iostream>
#include<cstring>
using namespace std;
int T,m,n,ans,f[55][55][55][55];
string a,b;
int main(){
//	freopen("string.in","r",stdin);
//	freopen("string.out","w",stdout);ios::sync_with_stdio(false);cin>>T;while(T--){memset(f,0,sizeof(f));cin>>a>>b;a=' '+a;b=' '+b;n=a.size()-1;m=b.size()-1;for(int lena=0;lena<=n;lena++){for(int i=1;i<=n-lena+1;i++){int j=i+lena-1;for(int lenb=0;lenb<=m;lenb++){for(int x=1;x<=m-lenb+1;x++){int y=x+lenb-1;if(lena+lenb==1) f[i][j][x][y]=1;if(lena+lenb==2){if(!lena&&b[x]==b[y]) f[i][j][x][y]=2;else if(!lenb&&a[i]==a[j]) f[i][j][x][y]=2;else if(lena&&lenb&&a[i]==b[x]) f[i][j][x][y]=2;}if(lena+lenb!=f[i][j][x][y]) continue;if(i>1&&j<n&&a[i-1]==a[j+1]) f[i-1][j+1][x][y]=max(f[i-1][j+1][x][y],f[i][j][x][y]+2);if(x>1&&y<m&&b[x-1]==b[y+1]) f[i][j][x-1][y+1]=max(f[i][j][x-1][y+1],f[i][j][x][y]+2);if(i>1&&y<m&&a[i-1]==b[y+1]) f[i-1][j][x][y+1]=max(f[i-1][j][x][y+1],f[i][j][x][y]+2);if(x>1&&j<n&&b[x-1]==a[j+1]) f[i][j+1][x-1][y]=max(f[i][j+1][x-1][y],f[i][j][x][y]+2);if(!lena&&y<m&&a[i]==b[y+1]) f[i][i][x][y+1]=max(f[i][i][x][y+1],f[i][j][x][y]+2);if(!lena&&x>1&&a[i]==b[x-1]) f[i][i][x-1][y]=max(f[i][i][x-1][y],f[i][j][x][y]+2);if(!lenb&&j<n&&b[x]==a[j+1]) f[i][j+1][x][x]=max(f[i][j+1][x][x],f[i][j][x][y]+2);if(!lenb&&i>1&&b[x]==a[i-1]) f[i-1][j][x][x]=max(f[i-1][j][x][x],f[i][j][x][y]+2);}}}}int ans=0;for(int i=1;i<=n;i++){for(int j=i-1;j<=n;j++){for(int x=1;x<=m;x++){for(int y=x-1;y<=m;y++){ans=max(ans,f[i][j][x][y]);}}}}cout<<ans<<'\n';}return 0;
}//分讨(较慢) 

暴搜:

听说或许可以构造数据 \(hack\) 掉?

但目前看来的确是最优解无疑了。

我们分别枚举 \(a,b\) 串的回文中心(记得特判一下回文串长度为偶数的情况呀),然后跟上面的 \(dp\) 转移相似,分别向左右暴搜就行了。

复杂度为 \(O(能过)\)(其实是我不会算😛)

代码:

$code$
#include<iostream>
using namespace std;
int T,m,n,ans;
string a,b;
inline void dfs(int la,int ra,int lb,int rb,int len){ans=max(ans,len);if(la>=1&&ra<=n&&a[la]==a[ra]) dfs(la-1,ra+1,lb,rb,len+2);if(la>=1&&rb<=m&&a[la]==b[rb]) dfs(la-1,ra,lb,rb+1,len+2);if(lb>=1&&ra<=n&&b[lb]==a[ra]) dfs(la,ra+1,lb-1,rb,len+2);if(lb>=1&&rb<=m&&b[lb]==b[rb]) dfs(la,ra,lb-1,rb+1,len+2);}
int main(){
//	freopen("string.in","r",stdin);
//	freopen("string.out","w",stdout);ios::sync_with_stdio(false);cin>>T;while(T--){ans=0;cin>>a>>b;a=' '+a;b=' '+b;n=a.size()-1;m=b.size()-1;for(int i=1;i<=n+1;i++){for(int j=1;j<=m+1;j++){dfs(i-1,i,j-1,j,0);if(i!=n+1) dfs(i-1,i+1,j-1,j,1);if(j!=m+1) dfs(i-1,i,j-1,j+1,1);//回文长度为偶数 }}cout<<ans<<'\n';}return 0;
}//暴搜(最快) 

一组 \(hack\) 数据

T2:数排列(perm)

思路:

嘿,有个 \(O(n^3)\) 的做法没听,当时光顾着笑(一些不明事物)了。这里只提供 \(O(n^2)\) 的做法。

我们设 \(f_{i,j}\) 表示数字 \(i\) 放到 \(j\) 的位置上的合法方案数,然后再前/后缀和优化一下撒~~

对于一个数 \(i\) ,若 \(s_i=1\) ,则我们设 \(f_{i,j}\) 表示 \(i\) 放在前 \(j\) 个位置的方案数 \(dp_{i,j}=\sum_{j=1}^{i} dp_{i-1,j}\)

代码:

$code$
#include<iostream>
using namespace std;
const int N=2025,mod=1e9+7;
int n,s[N],f[N][N],ans;
int main(){freopen("perm.in","r",stdin);freopen("perm.out","w",stdout);ios::sync_with_stdio(false);cin>>n;f[0][1]=1;for(int i=1;i<n;i++) cin>>s[i];for(int i=1;i<n;i++){if(s[i]==1) for(int j=2;j<=i+1;j++) f[i][j]=(f[i][j-1]+f[i-1][j-1])%mod;else for(int j=i;j>=1;j--) f[i][j]=(f[i][j+1]+f[i-1][j])%mod;}for(int i=1;i<=n;i++) ans=(ans+f[n-1][i])%mod;cout<<ans<<'\n';return 0;
}//O(n^2)

T3:树上的背包(knapsack)

思路:

这里提供根号分治和折半搜索两种思路。

折半搜索:

代码:

$code$
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N=1e5+5;
int n,Q,x,L,cnt,tot1,tot2,ans,v[N],w[N],vl[N],wei[N];
struct flower{int val,weight;bool operator<(const flower &css)const{if(weight!=css.weight) return weight<css.weight;else return val<css.val;}
}a[N],s1[N],s2[N];
inline void dfs1(int pos,int weight,int val){if(weight>L) return ;if(pos==cnt/2+1){s1[++tot1]={val,weight};return ;}dfs1(pos+1,weight,val);dfs1(pos+1,weight+a[pos].weight,val+a[pos].val);
}
inline void dfs2(int pos,int weight,int val){if(weight>L) return ;if(pos==cnt+1){s2[++tot2]={val,weight};return ;}dfs2(pos+1,weight,val);dfs2(pos+1,weight+a[pos].weight,val+a[pos].val);
}
signed main(){freopen("knapsack.in","r",stdin);freopen("knapsack.out","w",stdout);ios::sync_with_stdio(false);cin>>n;for(int i=1;i<=n;i++) cin>>v[i]>>w[i];cin>>Q;while(Q--){tot1=tot2=ans=cnt=0;cin>>x>>L;a[++cnt]={0,0};while(x){a[++cnt]=(flower){v[x],w[x]};x/=2;}dfs1(1,0,0);dfs2(cnt/2+1,0,0);sort(s1+1,s1+tot1+1);sort(s2+1,s2+tot2+1);for(int i=1;i<=tot2;i++) s2[i].val=max(s2[i].val,s2[i-1].val);for(int i=1,j=tot2;i<=tot1;i++){while(j&&s2[j].weight+s1[i].weight>L) j--;if(s1[i].weight+s2[j].weight<=L) ans=max(ans,s1[i].val+s2[j].val);}cout<<ans<<'\n';}return 0;
}//折半搜 (慢) 
/*
3
1 2
2 3
3 4
3
1 1
2 5
3 515
123 119
129 120
132 112
126 109
118 103
115 109
102 100
130 120
105 105
132 115
104 102
107 107
127 116
121 104
121 115
8
8 234
9 244
10 226
11 227
12 240
13 237
14 206
15 227*/

根号分治:

代码:

$code$
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+5,maxn=511;
int n,x,l,Q,dp[520][N];
struct flower{int v,w;
}a[N];
inline int work(int x,int l){if(l<0) return -1e9;if(x<=maxn) return dp[x][l];return max(work(x>>1,l),work(x>>1,l-a[x].w)+a[x].v);
}
int main(){
//	freopen("knapsack.in","r",stdin);
//	freopen("knapsack.out","w",stdout);ios::sync_with_stdio(false);cin>>n;for(int i=1;i<=n;i++) cin>>a[i].v>>a[i].w;for(int i=1;i<=min(maxn,n);i++){if(i>>1) memcpy(dp[i],dp[i>>1],sizeof(dp[i]));for(int j=N-5;j>=a[i].w;j--){dp[i][j]=max(dp[i][j],dp[i][j-a[i].w]+a[i].v);}}cin>>Q;while(Q--){cin>>x>>l;cout<<work(x,l)<<'\n';}return 0;
}

T2,T3未完...

后言:

感谢gyh提醒。

祝阿联生日快乐呀~~

http://www.gsyq.cn/news/29749.html

相关文章:

  • 10 24(+第14场补题)
  • Luogu P5479 [BJOI2015] 隐身术 题解 [ 紫 ] [ 多维 DP ] [ 交换维度 ] [ 后缀数组 ] [ 哈希 ]
  • 杂题选做-3
  • Unity静态资源优化
  • 【Java】Spring @Transactional 事务失效:9大经典『陷阱』及终极解决方案
  • CSP-S2022
  • Hugo主题的修改和配置
  • 最小生成树 kruskal算法
  • Tarjan练习
  • 洛谷 P7011 Escape
  • 【Java-JMM】Happens-before原则
  • P6072 『MdOI R1』Path
  • P1601题解
  • 关于驻马店市 2025 中小学信息学竞赛的记录(入门级)(未完)
  • 游记——驻马店市2025中小学信息学竞赛(未完)
  • ABP - SqlSugar [SqlSugarModule、ISqlSugarClient、SqlSugarRepository]
  • Odoo18.0 对接 京东快递
  • 轮次检测模型 VoTurn-80M 开源,多模态融合架构;OpenAI 收购桌面助手 Sky:实时识别屏幕自然语言交互丨日报
  • SAP维护汇率的关键Tcode
  • 第4天(中等题 滑动窗口、哈希表)
  • 申威服务器安装Java11(swjdk-11u-9.ky10.sw_64.rpm)详细操作步骤(附安装包)
  • Linux下的拼音输入法 (3)
  • P2606 [ZJOI2010] 排列计数 分析
  • 实用指南:MacOS - Clang使用bits/stdc++.h - 非官方(竞赛用) - 通用方法
  • 设计模式:代码界的 “光之巨人” 养成指南(附 C++ 实战)
  • 详细介绍:17-Language Modeling with Gated Convolutional Networks
  • 算法分析--生成排列
  • 三大安全认证授权协议深度对比:OAuth、OpenID Connect与SAML
  • (简记)(自用)线段树区间拆分时间复杂度证明
  • SpringBoot整合缓存2-Redis