news 2026/2/16 23:37:54

UVa 11617 An Odd Love

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UVa 11617 An Odd Love

题目描述

春天到了,我们的朋友佩皮托(Pepito \texttt{Pepito}Pepito) 坠入了爱河。但他不确定她是否也爱他,于是他决定询问雏菊。他摘下一朵雏菊,交替说着“她爱我”和“她不爱我”,每说一句话就摘下一片花瓣。最后一片花瓣对应的那句话会告诉他他的爱是否得到了回应。

我们想帮助佩皮托总是得到“她爱我”的答案。因此,我们将通过摘掉花瓣数为偶数的雏菊上的一片花瓣,确保所有雏菊的花瓣数都是奇数。

我们有一片矩形的雏菊田,宽度为W WW,高度为H HH。田地的每个位置( w , h ) (w, h)(w,h)都有一朵雏菊,其中w = 1 , 2 , … , W w = 1, 2, \ldots, Ww=1,2,,Wh = 1 , 2 , … , H h = 1, 2, \ldots, Hh=1,2,,H。我们已经耐心地数了每朵雏菊的花瓣数P [ w , h ] P[w, h]P[w,h]

从田地的左上角(即位置( 1 , 1 ) (1, 1)(1,1))开始,你必须经过所有花瓣数为偶数的位置。如果你的当前位置是( w , h ) (w, h)(w,h),你只能做三种移动:向下( h + 1 ) (h + 1)(h+1)、向左( w − 1 ) (w - 1)(w1)和向右( w + 1 ) (w + 1)(w+1)

你的任务是计算经过所有花瓣数为偶数的位置所需的最少移动次数。

输入格式

第一行包含一个整数,表示测试用例的数量。

对于每个测试用例,第一行包含两个整数W WWH HH,用空格分隔。接下来是H HH行,每行包含W WW个数字(1 119 99),表示对应雏菊的花瓣数。

输出格式

对于每个测试用例,输出一个整数,表示最少移动次数。

样例输入

5 5 3 54578 36329 75241 9 1 759456785 2 2 22 22 6 6 777777 772777 777777 777727 727777 777777 7 7 1811181 1118811 1881111 8111111 1188181 1881181 1111111

样例输出

11 7 3 11 24

题目分析

问题本质

这道题可以抽象为一个网格遍历问题:

  1. 我们有一个H × W H \times WH×W的网格,每个格子有一个数字(1 119 99)。
  2. 我们需要从左上角( 1 , 1 ) (1, 1)(1,1)出发。
  3. 必须访问所有数字为偶数的格子(称为“目标点”)。
  4. 移动方式只能向下、向左或向右,不能向上。
  5. 需要计算最少移动步数。

关键约束

  1. 不能向上移动:这意味着我们只能从上往下逐行遍历,一旦离开某一行就无法返回。
  2. 必须访问所有偶数格子:这是我们的核心目标。
  3. 起点固定:从( 1 , 1 ) (1, 1)(1,1)开始。

观察与简化

由于不能向上移动,我们的路径必然是单调向下的。这意味着:

  1. 我们只能按行顺序访问:第1 11行 → 第2 22行 → … → 第H HH行。
  2. 对于每一行,如果有偶数格子需要访问,我们必须在该行内水平移动来访问所有这些格子。
  3. 对于没有偶数格子的行,我们只需从上一行的某个位置垂直向下移动一行,不需要水平移动。

进一步分析

对于一行中的多个偶数格子,访问它们的最优策略是:

  1. 从该行的某个进入点开始。
  2. 访问该行所有偶数格子。
  3. 离开该行,准备进入下一行。

在一行内访问所有目标点的最小水平移动距离取决于:

  • 进入点的列位置
  • 该行最左目标点的列位置
  • 该行最右目标点的列位置

实际上,对于一行有多个目标点的情况,访问所有点的最小水平移动方案只有两种:

  1. 先到最左目标点,然后一路向右访问到最右目标点。
  2. 先到最右目标点,然后一路向左访问到最左目标点。

状态设计

由于我们只关心有目标点的行(需要访问的行),可以忽略没有目标点的行,只需在行间转移时计算垂直移动距离。

定义状态:

  • d p [ i ] [ 0 ] dp[i][0]dp[i][0]:访问完第i ii个目标行后,停在该行最左目标点的最小步数。
  • d p [ i ] [ 1 ] dp[i][1]dp[i][1]:访问完第i ii个目标行后,停在该行最右目标点的最小步数。

状态转移

设第i − 1 i-1i1个目标行在第r i − 1 r_{i-1}ri1行,最左目标点在l i − 1 l_{i-1}li1列,最右目标点在r i − 1 r_{i-1}ri1列。
设第i ii个目标行在第r i r_iri行,最左目标点在l i l_ili列,最右目标点在r i r_iri列。
设两行之间的行距为g a p = r i − r i − 1 gap = r_i - r_{i-1}gap=riri1

d p [ i − 1 ] [ 0 ] dp[i-1][0]dp[i1][0](停在l i − 1 l_{i-1}li1)转移到d p [ i ] [ 0 ] dp[i][0]dp[i][0](停在l i l_ili):

  1. 方案 A:先到l i l_ili,然后向右访问到r i r_iri,再返回l i l_ili
    • 步数 =d p [ i − 1 ] [ 0 ] + g a p + ∣ l i − 1 − l i ∣ + ( r i − l i ) × 2 dp[i-1][0] + gap + |l_{i-1} - l_i| + (r_i - l_i) \times 2dp[i1][0]+gap+li1li+(rili)×2
  2. 方案 B:先到r i r_iri,然后向左访问到l i l_ili(自然停在l i l_ili)。
    • 步数 =d p [ i − 1 ] [ 0 ] + g a p + ∣ l i − 1 − r i ∣ + ( r i − l i ) dp[i-1][0] + gap + |l_{i-1} - r_i| + (r_i - l_i)dp[i1][0]+gap+li1ri+(rili)

d p [ i − 1 ] [ 0 ] dp[i-1][0]dp[i1][0]转移到d p [ i ] [ 1 ] dp[i][1]dp[i][1](停在r i r_iri):

  1. 方案 C:先到l i l_ili,然后向右访问到r i r_iri(自然停在r i r_iri)。
    • 步数 =d p [ i − 1 ] [ 0 ] + g a p + ∣ l i − 1 − l i ∣ + ( r i − l i ) dp[i-1][0] + gap + |l_{i-1} - l_i| + (r_i - l_i)dp[i1][0]+gap+li1li+(rili)
  2. 方案 D:先到r i r_iri,然后向左访问到l i l_ili,再返回r i r_iri
    • 步数 =d p [ i − 1 ] [ 0 ] + g a p + ∣ l i − 1 − r i ∣ + ( r i − l i ) × 2 dp[i-1][0] + gap + |l_{i-1} - r_i| + (r_i - l_i) \times 2dp[i1][0]+gap+li1ri+(rili)×2

d p [ i − 1 ] [ 1 ] dp[i-1][1]dp[i1][1]的转移类似,只需将l i − 1 l_{i-1}li1替换为r i − 1 r_{i-1}ri1

初始化

对于第一个目标行(假设在第f i r s t R o w firstRowfirstRow行):

  • 从起点( 1 , 1 ) (1, 1)(1,1)出发,需要先垂直向下移动f i r s t R o w − 1 firstRow - 1firstRow1行。
  • 然后按照上述两种方案访问该行的所有目标点。

最终答案

访问完所有目标行后,答案为min ⁡ ( d p [ m − 1 ] [ 0 ] , d p [ m − 1 ] [ 1 ] ) \min(dp[m-1][0], dp[m-1][1])min(dp[m1][0],dp[m1][1]),其中m mm是目标行的数量。

如果整个网格没有目标点,答案为0 00

算法步骤

  1. 读取输入,找出所有有偶数格子的行及其列位置。
  2. 如果没有目标行,输出0 00
  3. 初始化第一个目标行的d p dpdp值。
  4. 对于每个后续目标行,计算从上一目标行转移过来的最小步数。
  5. 输出最终的最小步数。

时间复杂度

  • 预处理:O ( H × W ) O(H \times W)O(H×W)
  • 动态规划:O ( m ) O(m)O(m),其中m mm是目标行的数量(m ≤ H m \leq HmH
  • 总时间复杂度:O ( H × W ) O(H \times W)O(H×W),在题目限制下完全可行。

代码实现

// An Odd Love// UVa ID: 11617// Verdict: Accepted// Submission Date: 2025-12-15// UVa Run Time: 0.010s//// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;intmain(){intt;cin>>t;while(t--){intW,H;cin>>W>>H;vector<string>grid(H);for(inti=0;i<H;i++)cin>>grid[i];// 存储有目标的行及其目标列vector<pair<int,vector<int>>>targetRows;for(inth=0;h<H;h++){vector<int>cols;for(intw=0;w<W;w++){if((grid[h][w]-'0')%2==0)cols.push_back(w);}if(!cols.empty()){targetRows.push_back({h,cols});}}// 如果没有目标行,答案就是0(从起点开始不用移动)if(targetRows.empty()){cout<<0<<endl;continue;}constintINF=1e9;intm=targetRows.size();vector<vector<int>>dp(m,vector<int>(2,INF));// 初始化第一个目标行intfirstRow=targetRows[0].first;vector<int>firstCols=targetRows[0].second;intfirstLeft=*min_element(firstCols.begin(),firstCols.end());intfirstRight=*max_element(firstCols.begin(),firstCols.end());intfirstRowDist=firstRight-firstLeft;// 从起点(0,0)到第一个目标行// 需要先向下移动firstRow行intdownSteps=firstRow;// 从第0行到第firstRow行// 停在左端点dp[0][0]=downSteps+firstLeft+firstRowDist+firstRowDist;// 下移,到最左,到最右,回最左dp[0][0]=min(dp[0][0],downSteps+firstRight+firstRowDist);// 下移,到最右,到最左// 停在右端点dp[0][1]=downSteps+firstLeft+firstRowDist;// 下移,到最左,到最右dp[0][1]=min(dp[0][1],downSteps+firstRight+firstRowDist+firstRowDist);// 下移,到最右,到最左,回最右// 处理后续目标行for(inti=1;i<m;i++){intprevRow=targetRows[i-1].first;vector<int>prevCols=targetRows[i-1].second;intprevLeft=*min_element(prevCols.begin(),prevCols.end());intprevRight=*max_element(prevCols.begin(),prevCols.end());intcurrRow=targetRows[i].first;vector<int>currCols=targetRows[i].second;intcurrLeft=*min_element(currCols.begin(),currCols.end());intcurrRight=*max_element(currCols.begin(),currCols.end());intcurrRowDist=currRight-currLeft;introwGap=currRow-prevRow;// 行间距离for(intprevSide=0;prevSide<2;prevSide++){if(dp[i-1][prevSide]==INF)continue;intprevCol=(prevSide==0)?prevLeft:prevRight;// 从上一目标行到当前目标行// 中间需要下移rowGap行// 我们可以选择在中间行的任意列移动// 对于停在左端点// 方案1:先到当前行最左,访问到最右,然后回到最左intcost1=dp[i-1][prevSide]+rowGap+abs(prevCol-currLeft)+currRowDist+currRowDist;// 方案2:先到当前行最右,访问到最左(停在最左)intcost2=dp[i-1][prevSide]+rowGap+abs(prevCol-currRight)+currRowDist;dp[i][0]=min(dp[i][0],min(cost1,cost2));// 对于停在右端点// 方案3:先到当前行最左,访问到最右(停在最右)intcost3=dp[i-1][prevSide]+rowGap+abs(prevCol-currLeft)+currRowDist;// 方案4:先到当前行最右,访问到最左,然后回到最右intcost4=dp[i-1][prevSide]+rowGap+abs(prevCol-currRight)+currRowDist+currRowDist;dp[i][1]=min(dp[i][1],min(cost3,cost4));}}intans=min(dp[m-1][0],dp[m-1][1]);cout<<ans<<endl;}return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/10 12:31:41

基于西门子PLC1200的钢板恒张力放卷收卷系统大揭秘

基于西门子PLC1200的钢板恒张力放卷收卷系统&#xff0c;一共有6种不同的要求&#xff0c;自己注意仔细甄别&#xff0c;防止交错在工业生产中&#xff0c;钢板的放卷与收卷过程保持恒张力至关重要&#xff0c;这直接影响到产品的质量与生产效率。西门子PLC1200凭借其强大的功能…

作者头像 李华
网站建设 2026/2/13 7:37:28

如何通过LangFlow降低AI开发门槛?一文读懂

如何通过 LangFlow 降低 AI 开发门槛&#xff1f; 在大模型技术席卷各行各业的今天&#xff0c;越来越多团队开始尝试构建基于大语言模型&#xff08;LLM&#xff09;的应用——从智能客服到知识库问答&#xff0c;从自动化报告生成到个性化推荐系统。然而&#xff0c;现实往往…

作者头像 李华
网站建设 2026/2/10 7:22:46

深度学习环境搭建指南:TensorFlow + 清华源 + Docker

深度学习环境搭建新范式&#xff1a;TensorFlow 清华源 Docker 在人工智能项目落地的过程中&#xff0c;最让人头疼的往往不是模型设计本身&#xff0c;而是“为什么代码在我机器上能跑&#xff0c;在服务器却报错&#xff1f;”——这个看似简单的问题背后&#xff0c;是依…

作者头像 李华
网站建设 2026/2/16 17:03:49

Qwen3-32B Docker镜像5分钟快速部署指南

Qwen3-32B Docker镜像5分钟快速部署指南 在智能研发工具逐渐成为标配的今天&#xff0c;你有没有遇到过这样的窘境&#xff1a;团队急需一个能读文档、写代码、解释复杂逻辑的AI助手&#xff0c;结果试了一圈开源模型&#xff0c;不是“上下文一长就失忆”&#xff0c;就是“连…

作者头像 李华
网站建设 2026/2/4 6:16:31

YOLOv8 Segmentation实例分割实战教程

YOLOv8 实例分割实战&#xff1a;从原理到工业落地 在现代智能视觉系统中&#xff0c;仅仅知道“哪里有什么”已经不够了。越来越多的应用场景要求我们回答&#xff1a;“它具体长什么样&#xff1f;”——这正是实例分割&#xff08;Instance Segmentation&#xff09;的核心使…

作者头像 李华
网站建设 2026/2/9 10:28:07

联通紫金专线200M适合哪些企业?

在当今数字化办公时代&#xff0c;网络连接的质量直接影响到企业的运营效率。对于不少公司来说&#xff0c;选择合适的网络专线就像是挑选一双合脚的鞋子&#xff0c;既要舒适又要实用。联通紫金专线200M作为市场上的一种选择&#xff0c;它是否能够满足你的需求呢?让我们一起…

作者头像 李华