UVa #12170 Easy Climb (例题9-25)

每次的调整都要依赖于上一次的决策,因此是一个多阶段决策问题。多节段决策如果不用打印解,最好用滚动数组优化


设 dp(i,j) 表示将第i个山坡高度改为j,还需要修改前i-1个山坡,所需要的代价总合。


然而 j 的可能性太多(d<10^9),因此这个状态的设计是不可行的。不过n的值却很小(n<100),因此可以从n上下手


深入分析问题,发现如果前后两个山坡的高度已经固定了,那么当前山坡调整后的高度应该属于[ max(h[i-1], h[i+1])-d , min(h[i-1], h[i+1])+d ]。如果这个区间为空则无解。如果山坡已经属于这个高度则不用调整,否则应调整到就近的边界值。


将这种情况扩展一下,可以发现改变后的山坡高度总能表示为h[p] + q*d,p属于[0,n-1],q属于(-n,n)。因此有用的高度值其实只有n*2n个。将所有高度值的可能性算出来存在数组x中,并去重和排序。设dp(i,j)表示将第i个山坡的高度改为x[j],还需要修改前i-1个山坡,所需要的代价总合。


转移方程:dp(i,j) = abs(h[i]-x[j]) + min{dp(i-1,y)},x[y]属于[x[j]-d, x[j]+d]。这里的各种定义有点绕。有些是根据数组x的下标定义,有些是直接根据山坡高度定义。需要谨慎区分。


当j固定时,y的定义域则是0-(n-1)上的一段连续子区间。当j从0移动到n-1时,y就形成了一个滑动窗口。只不过这个滑动窗口的左右边界的更新与之前的有些不同,并不是j每次移动都会固定的出一个、入一个,而是应进行判断。对于每个j,我们要取出y的定义域内dp[i-1][y]的最小值来更新dp[i][j]。


Rujia的滚动数组很好用。t和t^1的设计使得滚动数组的滚动方向不再受到限制。至于滑动窗口的实现,我直接用了普通的单调队列,而Rujia的代码中巧妙的使用了一个k,对数据特征进行了进一步剖析。


Run Time: 0.768s

#define UVa  "LT9-25.12170.cpp"		//Easy Climb
char fileIn[30] = UVa, fileOut[30] = UVa;

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>

using namespace std;

//Global Variables. Reset upon Each Case!
typedef long long LL;
const int maxn = 100 + 5, maxx = 2*maxn*maxn;
const LL INF = (1LL)<<60;
int T, n, nx;
LL d, h[maxn], x[maxx], dp[2][maxx];
/////

struct Monoqueue {
    LL q[maxx];
    int front, rear;
    Monoqueue():front(-1),rear(-1){}
    void maintain_left(LL v) {
        if(front != -1 && q[rear] == v) rear++;
    }
    void insert(LL v) {
        if(front == -1) {
            rear = front = 0;
            q[front] = v;
        }
        else {
            while(q[front] > v && front >= rear) front --;
            q[++front] = v;
        }
    }
    LL get_min() {
        return q[rear];
    }
};




int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &d);
        for(int i = 0; i < n; i ++) scanf("%lld", &h[i]);

        if(abs(h[0] - h[n-1]) > (n-1)*d) {
            cout << "impossible\n";
            continue;
        }

        nx = 0;
        for(int i = 0; i < n; i ++)
            for(LL j = -n+1; j < n; j ++)
                x[nx++] = h[i]+j*d;

        sort(x, x + nx);

        nx = unique(x, x+nx) - x;

        int t = 0;
        for(int i = 0; i < nx; i ++) {
            if(x[i] == h[0]) dp[0][i] = 0;
            else dp[0][i] = INF;
        }
        for(int i = 1; i < n; i ++) {
            Monoqueue mq = Monoqueue();
            int p1 = 0, p2 = 0;                 //p1 points to first element >= x-d. p2 points to first element > x+d
            for(int j = 0; j < nx; j ++) {
                while(p1 < nx && x[p1] < x[j] - d) {
                    mq.maintain_left(dp[t][p1]);            //pop out from monoqueue.
                    p1 ++;
                }
                if(p2 < p1) p2 = p1;
                while(p2 < nx && x[p2] <= x[j] + d) {
                    mq.insert(dp[t][p2]);                   //insert into monoque.
                    p2 ++;
                }
                if(mq.get_min() == INF) dp[t^1][j] = INF;
                else dp[t^1][j] = abs(x[j]-h[i]) + mq.get_min();
            }
            t^=1;
        }
        for(int i = 0; i < nx; i ++)
            if(x[i] == h[n-1]) cout<<dp[t][i]<<endl;
    }

    return 0;
}


import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler import statsmodels.api as sm # 用于计算P值 # ---------------------- # 1. 生成模拟数据(贴合转运场景) # ---------------------- np.random.seed(42) # 固定随机数,保证结果可复现 n = 500 # 样本量 data = pd.DataFrame({ # 自变量:候选影响因素 &#39;GCS评分&#39;: np.random.randint(3, 16, size=n), # GCS评分3-15分,越低病情越重 &#39;管道数量&#39;: np.random.randint(0, 6, size=n), # 0-5根管道 &#39;转运时长&#39;: np.random.randint(5, 60, size=n), # 5-60分钟 &#39;镇静程度&#39;: np.random.randint(0, 3, size=n), # 0=未镇静,1=浅镇静,2=深镇静 &#39;生命体征波动&#39;: np.random.randint(0, 3, size=n), # 0=稳定,1=轻度,2=显著 # 因变量:病情变化(问题1核心) # 模拟逻辑:GCS低、管道多、转运久、镇静不足、生命体征波动大→更易病情变化 &#39;病情变化&#39;: np.where( (np.random.rand(n) < 0.1) + # 基础概率 (data[&#39;GCS评分&#39;] < 9) * 0.3 + # GCS<9(昏迷)增加30%概率 (data[&#39;管道数量&#39;] > 2) * 0.2 + # 管道>2根增加20%概率 (data[&#39;转运时长&#39;] > 30) * 0.15 + # 转运>30分钟增加15%概率 (data[&#39;镇静程度&#39;] == 0) * 0.25 + # 未镇静增加25%概率 (data[&#39;生命体征波动&#39;] == 2) * 0.3 # 显著波动增加30%概率 > 0.5, 1, 0 ) }) # ---------------------- # 2. 逻辑回归筛选变量(计算P值,保留P<0.05) # ---------------------- # 提取自变量和因变量 X = data[[&#39;GCS评分&#39;, &#39;管道数量&#39;, &#39;转运时长&#39;, &#39;镇静程度&#39;, &#39;生命体征波动&#39;]] y = data[&#39;病情变化&#39;] # 用statsmodels计算P值(更适合筛选变量) X_sm = sm.add_constant(X) # 添加常数项(截距) logit_model = sm.Logit(y, X_sm) result = logit_model.fit() # 输出变量系数和P值 var_pvalues = result.pvalues.drop(&#39;const&#39;) # 排除常数项 significant_vars = var_pvalues[var_pvalues < 0.05].index.tolist() # 显著变量列表 print("步骤1:逻辑回归筛选的显著影响因素(P<0.05):") print(significant_vars) print("\n各变量P值:") print(var_pvalues.round(4)) # ---------------------- # 3. 可视化:变量重要性(系数绝对值) # ---------------------- plt.figure(figsize=(8, 5)) coef = result.params.drop(&#39;const&#39;).abs() # 系数绝对值(代表影响强度) sns.barplot(x=coef.index, y=coef.values) plt.title(&#39;逻辑回归筛选的变量重要性(系数绝对值)&#39;, fontsize=12) plt.ylabel(&#39;系数绝对值(越大影响越显著)&#39;) plt.xticks(rotation=30) plt.tight_layout() plt.show() from pgmpy.models import BayesianNetwork from pgmpy.estimators import HillClimbSearch, BicScore from pgmpy.inference import VariableElimination import networkx as nx # ---------------------- # 1. 准备数据(仅保留显著变量+新增不良事件因变量) # ---------------------- # 新增“不良事件”因变量(模拟:由病情变化、管道数量、镇静程度共同影响) data[&#39;不良事件&#39;] = np.where( (data[&#39;病情变化&#39;] == 1) * 0.4 + # 病情变化增加40%概率 (data[&#39;管道数量&#39;] > 2) * 0.3 + # 管道多增加30%概率 (data[&#39;镇静程度&#39;] == 0) * 0.25 # 未镇静增加25%概率 > 0.5, 1, 0 ) # 仅保留步骤1筛选的显著变量+结局变量 bn_data = data[significant_vars + [&#39;病情变化&#39;, &#39;不良事件&#39;]] # ---------------------- # 2. 构建贝叶斯网络(学习结构和参数) # ---------------------- # 用Hill-Climb算法学习网络结构(基于数据关联) hc = HillClimbSearch(bn_data) model = hc.estimate(scoring_method=BicScore(bn_data)) # 用BIC评分优化结构 # 学习条件概率表(CPT) from pgmpy.estimators import MaximumLikelihoodEstimator model.fit(bn_data, estimator=MaximumLikelihoodEstimator) # ---------------------- # 3. 可视化:网络关联路径 # ---------------------- plt.figure(figsize=(10, 6)) pos = nx.spring_layout(model) # 布局 nx.draw(model, pos, with_labels=True, node_size=3000, node_color=&#39;lightblue&#39;, font_size=10, arrowsize=20) # 标注关键路径(示例:手动标注临床关注的路径) critical_edges = [(&#39;GCS评分&#39;, &#39;生命体征波动&#39;), (&#39;生命体征波动&#39;, &#39;病情变化&#39;), (&#39;管道数量&#39;, &#39;不良事件&#39;), (&#39;病情变化&#39;, &#39;不良事件&#39;)] nx.draw_networkx_edges(model, pos, edgelist=critical_edges, edge_color=&#39;red&#39;, width=2) plt.title(&#39;贝叶斯网络:转运相关因素关联路径(红色为关键路径)&#39;, fontsize=12) plt.tight_layout() plt.show() # ---------------------- # 4. 推理:关键路径概率验证 # ---------------------- infer = VariableElimination(model) # 例:计算“GCS评分低(<9)”时,“不良事件”的发生概率 prob_bad = infer.query(variables=[&#39;不良事件&#39;], evidence={&#39;GCS评分&#39;: 6}) # GCS=6(低) print("\n步骤2:贝叶斯网络推理结果:") print(f"GCS评分低(6分)时,不良事件发生概率:{prob_bad.values[1]:.2f}") from scipy.stats import spearmanr # ---------------------- # 1. 提取贝叶斯网络中的核心变量对 # ---------------------- # 基于贝叶斯网络的关键路径,选择待分析的变量对 core_pairs = [ (&#39;GCS评分&#39;, &#39;生命体征波动&#39;), # GCS低是否与生命体征波动相关 (&#39;生命体征波动&#39;, &#39;病情变化&#39;), # 波动是否与病情变化相关 (&#39;管道数量&#39;, &#39;不良事件&#39;) # 管道数量是否与不良事件相关 ] # ---------------------- # 2. 斯皮尔曼相关分析 # ---------------------- corr_results = [] for var1, var2 in core_pairs: rho, p = spearmanr(data[var1], data[var2]) corr_results.append({ &#39;变量对&#39;: f"{var1}-{var2}", &#39;相关系数ρ&#39;: round(rho, 2), &#39;P值&#39;: round(p, 4), &#39;关联强度&#39;: &#39;强&#39; if abs(rho) > 0.5 else &#39;中&#39; if abs(rho) > 0.3 else &#39;弱&#39; }) # 输出结果 corr_df = pd.DataFrame(corr_results) print("\n步骤3:斯皮尔曼相关分析结果(核心变量对):") print(corr_df) # ---------------------- # 3. 可视化:相关系数热力图 # ---------------------- plt.figure(figsize=(6, 4)) corr_matrix = data[significant_vars + [&#39;病情变化&#39;, &#39;不良事件&#39;]].corr(method=&#39;spearman&#39;) sns.heatmap(corr_matrix, annot=True, cmap=&#39;coolwarm&#39;, vmin=-1, vmax=1) plt.title(&#39;斯皮尔曼相关系数热力图(核心变量)&#39;, fontsize=12) plt.tight_layout() plt.show()怎么改,不能运行
最新发布
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值