洛谷团队内部比赛解题报告

今日运势:
这里写图片描述
今日适合重构代码,于是乎我这四道四道题目的代码真的重构了很多遍(因为测试数据都过不去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;
}

我是华丽丽的结束分割线


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值