暑假训练赛1
Posted C_YCBX Py_YYDS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假训练赛1相关的知识,希望对你有一定的参考价值。
A 金属采集
搜了下。发现是树形dp,这玩意确实不会,也没做过。。。所以暂时也看不懂。。
B 一元三次方程求解
- 解题思路:用浮点数从负数一直枚举到正数,根据数学中,存在根的位置,左右两边的位置的函数值相乘肯定是小于0(异号)的。
#include <bits/stdc++.h>
using namespace std;
double a,b,c,d,i;
int main()
{
cin >>a>>b>>c>>d;
//先用枚举初步确定三个根的范围,
//比如f[i]*f[i+1]<0则可知道[i,i+1]之间有一个根
for(i=-100;i<=100;i+=0.01){
double x1=i-0.005,x2=i+0.005;
if((x1*x1*x1+b/a*x1*x1+c/a*x1+d/a)*(x2*x2*x2+b/a*x2*x2+c/a*x2+d/a)<0)
printf("%.2f ",i);
}
return 0;
}
C 求先序排列
- 解题思路:这类题目在面试中很常见,给出中序+先序或者后序,要确定一个二叉树序列,同样这类题无论给的是中序+先序,还是中序+后序,我们都可以通过各种遍历的特性以及递归分治来组建该二叉树。
- 先序排列:根节点+左子树+右子树
- 中序排列:左子树+根节点+右子树
- 后序排列:左子树+右子树+根节点
最后我们根据先序\\后序能瞬间确定根节点的位置,而在根据中序之中根节点的位置,我们便能确定左子树和右子树的位置了,然后递归分治便可实现一个构建二叉树的方式,而此题需要构建先序排列,则需要在左右子树均未形成的位置将根节点打印即可完成此题。
- 小细节:寻找中序中根节点的位置可以通过哈希表提前记录下来(这种题中序一般就是个工具人)。
#include <bits/stdc++.h>
using namespace std;
string post,in;
int memo[26];
void pre(int root, int start, int end) {
if(start > end) return ;
int i = memo[post[root]-'A'];
printf("%c", post[root]);
//左子树
pre(root - 1 - end + i, start, i - 1);
//右子树
pre(root - 1, i + 1, end);
}
int main() {
cin>>in>>post;
memset(memo,0,sizeof(memo));
for(int i=0;i<in.size();i++){
memo[in[i]-'A'] = i;
}
pre(post.size()-1,0,post.size()-1);
return 0;
}
D 单词接龙
- 解题思路:三个步骤
- 用邻接矩阵预处理记录任意两个编号单词的公共最小接壤尾部(当最小接壤尾部为前一个单词自身,则不可)。
- 创建记录被使用编号的表。
- dfs爆搜结果,注意每次选择需要更新使用次数以及答案,能不能选要看是否尾部存在最小接壤以及被选的次数不能超过两次!
关于邻接矩阵的更新方法:很普遍的可以用string的substr()方法进行枚举遍历出最小结果然后通过二维数组记录,我这边采用的有穷状态自动机的思想进行更新的答案。。。学过一点自动机的应该都能懂。。这是我所能想到的两种更新方法,其他方法你们也可以尝试。
我也是最近写自动机的题目写了一些,所以啥都想着自动机。。。
#include<bits/stdc++.h>
using namespace std;
int n;char start;
vector<string>s(25);
int mp[25][25];
int used[25]; // 每个单词使用的次数
int ans = 0;
//根据词语接龙的末尾有无重合字符,枚举并添加下一个选择的单词更新长度
void dfs(int pos,int len)
{
ans = max(len, ans);
//记录使用次数
used[pos] ++ ;
for (int i = 0; i < n; i ++ )
//能有公共尾部,且使用次数小于2则可选,此时长度会变成len+s[i].size()-mp[pos][i]
if (mp[pos][i] && used[i] < 2)
dfs(i, len+(int)s[i].size()-mp[pos][i]);
used[pos] -- ; // 撤销使用次数
}
//用于任意更新两个单词邻接矩阵(存储两个单词的末尾最小公共长度)的函数
void get_same(const string&s1,const string&s2,int x1,int x2){
int state = 1;
int len1 = s1.size(),len2 = s2.size();
int i = 0,j=0;
int cnt = 0;
int res = INT_MAX;
while(i<len1&&j<len2){
//状态1都是两个字符不相等的情况,一旦出现相等则进入状态2
if(state==1){
cnt = 0;
j = 0;
if(s1[i]==s2[j])
state = 2;
else i++;
}//处于状态2的都是两个字符现在相等的状态,如果最后到可以弹出的状态就继续进行判断
else if(state==2){
if(s1[i]==s2[j]){
i++;
j++;
cnt++;
if(i==len1){
res = min(res,cnt);
j = 0;
i = i-cnt+1;
state = 1;
}
}else{
state = 1;
}
}
}
if(res!=len1&&res!=INT_MAX)
mp[x1][x2] = res;
}
//构建邻接矩阵,记录任意两个单词是否可以连接以及最大重复
void pre_handle(){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
get_same(s[i],s[j],i,j);
}
}
}
int main(){
memset(mp, 0,sizeof(mp));
memset(used,0,sizeof(used));
cin>>n;
for(int i=0; i<n; i++)
cin>>s[i];
cin>>start;
pre_handle();
//寻找开头的单词进行爆搜
for(int i=0;i<n;i++){
if(s[i][0]==start){
dfs(i,(int)s[i].size());
}
}
cout<<ans;
}
E 校门外的树
- 解题思路:
这定眼一看就是区间除交集的题目,而且数据量不大,直接用数组映射区间元素即可(具体看代码应该很容易理解)
前几天看过的各类区间调度问题的总结文章:区间调度问题
#include<bits/stdc++.h>
using namespace std;
int check[10001];
int L,M;
int main(){
memset(check,0,sizeof(check));
cin>>L>>M;
for(int i=0;i<M;i++){
int a,b;
cin>>a>>b;
for(int j=a;j<=b;j++){
check[j]++;
}
}
//记录要砍多少颗树
int count = 0;
for(int i=0;i<=L;i++)
if(check[i])
count++;
cout<<L+1-count;
}
F 比赛安排
- 题目分析:注意是输入一个n,然后就确定有了2的n次方个队伍,而需要你用2的n次方-1天来安排比赛。
这种安排两个队伍进行比赛的问题,很明显就是 C n 2 C^{2}_{n} Cn2求组合的问题。 - 重点注意两点:
- 以天为单位进行输出,每天每支队伍只能比赛一次(故每天的组合需要判断是否已经比过赛,然后再进行组合)
- 曾经已经比过赛的队伍,不能再重复进行比赛(所以每天输出的组合不能相同,也就是需要一个记录一对数据的哈希表(当然此处数据量小,可以用大容量数组代替也不会产生哈希冲突))
#include <bits/stdc++.h>
using namespace std;
int main() {
//其中check1用于标记在该天,该队伍比过赛了,check2用于标记两只队伍已经比过赛了
//由于数据量都不大,均可用数组代哈希表
int n, m;
//check1是用于某天的哈希表,所以换一天的时候需要重置
//check2则是用于整个天数过程哈希表,故不需重置
bool check1[1000], check2[10000] = {0};
cin >> n;
m = pow(2, n);
//外层循环记录当前为第几天,里层相当于从m个里面取两个进行输出,但要保证前几天已经比过赛的队伍不能输出,且在该天不能出现一只队伍比两次赛
for (int i = 1; i <= m - 1; i++) {
memset(check1, 0, sizeof(check1));
printf("<%d>", i);
for (int j = 1; j <= m; j++) {
if (check1[j] == 0) {
for (int k = j + 1; k <= m; k++) {
if (check2[j * 100 + k] == 0 && check1[k] == 0) {
printf("%d-%d ", j, k);
check2[j * 100 + k] = 1;
check1[j] = check1[k] = 1;
break;
}
}
}
}
cout << endl;
}
return 0;
}
以上是关于暑假训练赛1的主要内容,如果未能解决你的问题,请参考以下文章
哈理工2015 暑假训练赛 zoj 2976 Light Bulbs