今日运势:
今日适合重构代码,于是乎我这四道四道题目的代码真的重构了很多遍(因为测试数据都过不去awa)
好了废话不多说,进入正题
A:
题目大意:
有n个y在y数组中,求一个Y使得y数组中所有数据与Y的距离(即差的绝对值)总和最小并输出这个最小的总和
思路:一道简单的数学题,可以证明,Y的建立点就在所有点的中央,即y[n/2],证明方法这里就不说了(其实就是不会证明233咳咳),反正我们只需要知道,最优的Y一定在y[n/2]与y[n/2+1]之间(含这两个点)
AC代码:
#include<bits/stdc++.h>
using namespace std;
int mans;//答案
int y[11111],x;//因为x对我们来说没有任何卵用,所以把他当个临时变量
int n;//输油点数量
int f(int ff) {//求和函数
int ans=0;
for(int i=0; i<n; i++)
ans+=abs(y[i]-ff);//差的绝对值
return ans;
}
int main() {
//——————————————————————————————
cin>>n;
for(int i=0; i<n; i++) cin>>x>>y[i];//如果觉得这个x看着不爽可以使用scanf("%*d%d",&y[i]);来输入(强迫症福利)
//------------------------------至此,输入部分结束
sort(y,y+n);//默认按从小到大排
//------------------------------至此,排序部分结束
int t=y[n>>1];//这里取y[n/2]
mans=f(t);//答案赋值,直接输出也无妨
cout<<mans<<endl;
return 0;
//——————————————————————————————至此,整个处理与输出部分结束,整个程序结束
}
本题难度:入门难度(个人意见)
程序思路部分没什么好说的了,这里给一个小技巧,如果读入的一个数据不想传递给某一个变量(即假信息或者说废数据)那么可以在scanf(“”)引号内%号的后面加一个*号,就能做到吃数据的效果啦。
B:
题目大意:
有一个未完成的等式:1 2 3 4 5 6 7 8 9=N 空格(1前面没有空格)内可以填入+,-,也可以不填。 编程找出输入某个整数 N 后使等式成立的所有方案的总数。(貌似题目描述就是大意说)
思路:暴搜,枚举出所有可能的情况并一个一个判断(反正就9个数字咱不怕超时)
AC代码(judge函数写的很丑大犇勿喷):
#include<bits/stdc++.h>
using namespace std;
int n,ans,a[11];//8个空格放的符号(空或+-)
bool judge() {//判断函数
int ans;//保存结果
int b[11],tot=0;//b数组保存运算数,tot保存运算数数量
b[0]=1;//先赋初值1
for(int i=1; i<=8; i++)//遍历8个空位
if(!a[i])b[tot]=b[tot]*10+i+1;//如果不是+-(对应1,2),当前运算数加一位
else b[++tot]=i+1;//否则新建一个运算数
/*for(int i=1; i<=8; i++)//检测用的
if(!a[i])cout<<i;
else if(a[i]==1)cout<<i<<"+";
else cout<<i<<"-";
cout<<9<<endl;*/
/*for(int i=0; i<=tot; i++)//同上
cout<<b[i]<<" ";
getchar();*/
ans=b[0];//把第一个运算数赋给ans
tot=1;//重置计数器
for(int i=1; i<=8; i++)//依然是枚举八个空位
if(a[i])if(a[i]==1)ans+=b[tot++];//如果是+号
else ans-=b[tot++];//如果是减号
/*cout<<ans<<endl;*/
if(ans==n)return 1;//如果ans==n返回true(即非零值)
return 0;
}
void dfs(int h) {
if(h==9) {//如果8个空都填满了
if(judge())ans++;//如果该方案成立,方案种数+1
return;//不需要再执行
}
for(int i=2; i>=0; i--) {//从全部填-开始枚举,改成顺序也无妨(这里是因为我给自己卡了一组数据才改的结果发现那数据无解)
a[h]=i;//赋值
dfs(h+1);//递归
}
}
int main() {
cin>>n;//输入结果
dfs(1);//递归
cout<<ans<<endl;//输出
return 0;
}
本题难度:普及-(个人意见)
最后提一下:暴搜是个好方法,比赛时如果没有思路不妨试试这个awa
C:
题目大意:
给出一棵二叉树的中序与后序排列。求出它的先序排列。
(描述=大意系列)
思路:使用递归方法解决,函数带四个参数:l1,r1,l2,r2,表示中序遍历为l1-r1,后序遍历为l2-r2的树,使用字符串保存中序与后序遍历,并通过查找后序遍历中R在中序遍历中的位置,将其分成左右两部分进行递归,递归边界(l1>r1或l1=r1)
AC代码:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
string mid,last;
void write(int l1,int r1,int l2,int r2) {
if(l1>r1)return;//递归边界
if(l1==r1) {//要是只有这一个了
cout<<mid[l1];
return;//停止递归
}
int k=mid.find(last[r2]); //寻找到当前区域的根在中序遍历的位置
cout<<last[r2];//为什么这样输出?因为l2-r2的后序遍历的根就是最后一个,先序遍历的输出就是 根 左 右 所以直接输出根
/*
流程图(蒟蒻手打)
1.B A D C 2. B A D C 3. B A D C 4. B A D C
l1 r1 lr1 l1 r1 lr1
↑ ↑ ↑ ↑
输出A 输出B 输出C 输出D
B D C A B D C A B D C A B D C A
l2 r2 l2 r2
↑ ↑ ↑
*/
write(l1,k-1,l2,l2+k-l1-1);//递归输出左区间
write(k+1,r1,l2+k-l1,r2-1);//递归输出右区间
}
int main() {
int k;//存储节点数目
cin>>mid>>last;//输入中序与后续遍历
k=mid.length();
write(0,k-1,0,k-1);//调用递归
return 0;
}
本题难度:普及/提高-(个人意见)
D:
题目大意:
SSOI要设计n道比赛试题,题目可以在m个算法专题中选择。由于算法专题有限,SSOI不得不重复选择一些算法专题。
设计不同算法专题的试题所花的时间不同。具体地说,对于某个算法专题i,若SSOI计划一共设计x道试题,则完成该算法专题的试题总共需要花费Ai*x^Bi个单位时间(系数Ai和指数Bi均为正整数)。给定与每一个算法专题相对应的Ai和Bi的值,请帮助SSOI计算出如何选择试题的算法专题使得他可以花费最少的时间完成这n道试题。(我也不知道怎么简短了233)
思路:
这题应该是这次比赛中唯一一道真正对应了NOIP第四题的水平,一道DP多重背包题,使用三重循环解决。第一重i枚举当前所选择的类别,第二重j枚举背包的大小,第三重k枚举选择的数量,状态转移方程f[j]=min(f[j],f[k]+power[j-k][B[i]]*A[i]);A[i]B[i]含义如题,power数组保存幂。最后f[n]就是答案。
AC代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll power[222][22],f[222];
int n,m,A[22],B[22];
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++)
power[i][0]=1,f[i]=0x3f3f3f3f3f3f3f;//初始化为伪极大值,即INF+INF仍然是INF而不是-INF
for(int i=0; i<=n; i++)
for(int j=1; j<=5; j++)
power[i][j] = power[i][j - 1] * i;//打表生成幂
for(int i=1; i<=m; i++)
cin>>A[i]>>B[i];//输入
for(int i=1; i<=m; i++)//由于DP的无后效性,所以输入也可以放在这里执行,且无需单独开一个数组
for(int j=n; j>=1; j--)//用逆序(01)枚举背包的空间
for(int k=0; k<j; k++)//枚举可以选择的题数
//为什么k<j呢?因为这里一道题目的重量为1,即占用1背包空间
f[j]=min(f[j],f[k]+power[j-k][B[i]]*A[i]);//状态转移方程
/*for(int i=1;i<=n;i++)
cout<<f[i]<<" ";*///测试用
cout<<f[n]<<endl;//输出
return 0;
}
我是华丽丽的结束分割线