[Offer收割]编程练习赛27

元素魔法

题解

针对 a1g(x1)+a2g(x2)+... 的求职,如果 a1+a2+...=M 的话,可以使用拉格朗日乘子法【此方法需要大家有高数基础,在学高数时应该学过,大概的证明思路,就是对每一个项求一次偏导,然后求极值】
然后上面可以得出一个结论:
xi 为一个已知值,而 ai 只和为一个已知值时,得出
sum(xi)sum(ai)=xiai ,取到最优解。

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>

using namespace std;
const int MAXN = 2e5 + 5;
int k;
double a, w[MAXN];
double ret[MAXN];
int main(){
    while(~scanf("%d%lf", &k, &a)){
        double sum = 0;
        for(int i = 0;i < k;i ++){
            scanf("%lf", &w[i]);
            sum += w[i];
        }
        double bl = sum / a;
        double ans = 0;
        for(int i = 0;i < k;i ++){
            ret[i] = w[i] / bl;
            ans += w[i] * log(ret[i]);
        }
        printf("%.5f\n", ans);
        for(int i = 0;i < k;i ++){
            printf("%.5f%c", ret[i], i == k - 1 ? '\n' : ' ');
        }
    }
    return 0;
}

两个机器人

题解

很简单的BFS,然后用vis优化一下时间,防止重复访问,如果你还想优化的话,可以加一轮检测就是遇到两个障碍就抛弃

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>

using namespace std;
const int MAXN = 50 + 5;
char MP[MAXN][MAXN];
bool vis[MAXN][MAXN][MAXN][MAXN];
int N, M;
int adx[2] = {1, 0};
int ady[2] = {0, 1};
int bdx[2] = {-1, 0};
int bdy[2] = {0, -1};
struct State{
    int Ax, Ay, Bx, By;
    int step;
    State(int Ax, int Ay, int Bx, int By, int step):Ax(Ax), Ay(Ay), Bx(Bx), By(By), step(step){}
};

int BFS(){
    queue<State> Q;
    while(!Q.empty()) Q.pop();
    Q.push(State(0, 0, N - 1, M - 1, 0));
    vis[0][0][N - 1][M - 1] = true;
    while(!Q.empty()){
        //printf("-----------\n");
        State p = Q.front();
        Q.pop();
        if(p.Bx == 0 && p.By == 0 && p.Ax == N - 1&& p.Ay == M - 1){
            return p.step;
        }
        //此处加了剪枝
        int flagA = 0;
        for(int i = 0;i < 2;i ++){
            int nAx = p.Ax + adx[i];
            int nAy = p.Ay + ady[i];
            if(nAx < 0 || nAx >= N || nAy < 0 || nAy >= M || MP[nAx][nAy] == '1'){
                flagA ++;
            }
        }
        if(p.Ax != N - 1 && p.Ay != M - 1 && flagA == 2) continue;
        int flagB = 0;
        for(int i = 0;i < 2;i ++){
            int nBx = p.Bx + bdx[i];
            int nBy = p.By + bdy[i];
            if(nBx < 0 || nBx >= N || nBy < 0 || nBy >= M || MP[nBx][nBy] == '1') {
                flagB ++;
            }
        }
        if(p.Bx != 0 && p.By != 0 && flagB == 2) continue;
        //此处加了剪枝
        for(int i = 0;i < 2;i ++){
            int nAx = p.Ax + adx[i];
            int nAy = p.Ay + ady[i];
            int nBx = p.Bx + bdx[i];
            int nBy = p.By + bdy[i];
            int flag = 0;
            if(nBx == p.Ax && nBy == p.Ay && nAx == p.Bx && nAy == p.By) continue;
            if(nAx == nBx && nAy == nBy) continue;
            if(nAx < 0 || nAx >= N || nAy < 0 || nAy >= M || MP[nAx][nAy] == '1'){
                nAx = p.Ax;
                nAy = p.Ay;
                flag ++;
            }
            if(nBx < 0 || nBx >= N || nBy < 0 || nBy >= M || MP[nBx][nBy] == '1') {
                nBx = p.Bx;
                nBy = p.By;
                flag ++;
            }
            if(flag == 2) continue;
            if(vis[nAx][nAy][nBx][nBy]) continue;
            vis[nAx][nAy][nBx][nBy] = true;
            Q.push(State(nAx, nAy, nBx, nBy, p.step + 1));
        }
    }
    return -1;
}

int main(){
    while(~scanf("%d%d", &N, &M)){
        for(int i = 0;i < N;i ++){
            scanf("%s", MP[i]);
        }
        memset(vis, false, sizeof(vis));
        int ret = BFS();
        printf("%d\n", ret);
    }
    return 0;
}

子树中的最小权值

题解

正如大佬们经常说的,随便搞搞就可以了,很简单,这里引入一些前导知识,首先求一棵树上任意两个节点的路径上的最小值,大家应该都会求,方法呢,一般有几种:

  1. 用树链剖分,简直裸模板
  2. DFS序,然后用线段树啊,RMQ来实现

这道题目,我个人的实现思路很简单,就是DFS序,然后一个节点通过ps[],和pe[]来记录该节点包含的子节点的起始节点编号和终止节点编号,然后如果求解这个节点的最小值或者最大值直接query(ps[], pe[])就可以实现了

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define FIN freopen("input.txt", "r", stdin)

using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
int ps[MAXN];
int pe[MAXN];
int fp[MAXN];
int nv[MAXN];
int summ[MAXN << 2];
struct Edge {
    int u, v, nxt;
} E[MAXN << 1];
int Head[MAXN], tot, sz;

void edge_init() {
    tot = 0;
    memset(Head, -1, sizeof(Head));
}
void add_edge(int u, int v) {
    E[tot].u = u;
    E[tot].v = v;
    E[tot].nxt = Head[u];
    Head[u] = tot ++;
}

void xds_init() {
    sz = 0;
}
//DFS序
void dfs(int u, int fa) {
    ps[u] = ++ sz;
    fp[ps[u]] = u;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == fa) continue;
        dfs(v, u);
    }
    pe[u] = sz;
}
void push_up(int rt) {
    summ[rt] = min(summ[rt << 1], summ[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
    if(l == r) {
        summ[rt] = nv[fp[l]];
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}



void update(int p, int v, int l, int r, int rt) {
    if(l == r) {
        summ[rt] = v;
        return;
    }
    int mid = (l + r) >> 1;
    if(p <= mid) {
        update(p, v, lson);
    } else {
        update(p, v, rson);
    }
    push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        return summ[rt];
    }
    int mid = (l + r) >> 1;
    int ret = INF;
    if(L <= mid) {
        ret = min(ret, query(L, R, lson));
    }
    if(R > mid) {
        ret = min(ret, query(L, R, rson));
    }
    return ret;
}
int N;
int main() {
    //FIN;
    while(~scanf("%d", &N)) {
        for(int i = 1; i <= N; i ++) {
            scanf("%d", &nv[i]);
        }
        edge_init();
        xds_init();
        int x;
        for(int i = 2; i <= N; i ++) {
            scanf("%d", &x);
            add_edge(i, x);
            add_edge(x, i);
        }
        int root = 1;
        dfs(root, -1);
        build(1, sz, 1);
        int q, op, u;
        scanf("%d", &q);
        while(q --) {
            scanf("%d", &op);
            if(op == 1) {
                scanf("%d%d", &x, &u);
                update(ps[x], u, 1, sz, 1);
            } else {
                scanf("%d", &x);
                printf("%d\n", query(ps[x], pe[x], 1, sz, 1));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值