题目链接:nyoj543遥控器 或者nsoj5138遥控器
题目描述与生活中使用遥控器的境况基本一致,此处不再累述。
我的思路:
从x频道转到y频道有两种方法,取两者按键次数少者
转频道方法1、
直接按↑up键、↓down键(前提是该键可用),此时需比较从x频道到y频道按up与down按键次数。比如x = 1,y = 98这种情况显然就是按down时次数少。
转频道方法2、
x频道先跳转到中间频道z,再按↑up键、↓down键到y频道(把x跳到y的情况看成z==y),当然跳转需要 _键 及数字键有用,跳转到小于10的频道无需判断 _键(题目已知)。跳转时按键次数+up或down次数即为总按键次数。
先执行方法1,再从0~99循环方法2(即把0~99频道当成z),得出最小值即为所求。
改进:
发现每次都循环0~99显然是没必要的,比如x=23,y=52,先由方法1得到按键次数min(此处为29)
我们只需循环52-(min:29-3)~~52+(min:29-3) 即26~~78=52次即可,其中3是此处跳转到z时需要按键的次数,由于z<10时为1,为了简化问题复杂性干脆不减,即从y-min循环到y+min。循环需要一个辅助数组,比如当x=29, y=1时,min=28, 此时循环就是从73到29,注意是73->99->0->29的循环,故需要一个辅助数组维持循环。
代码如下:
#include <stdio.h>
#define INF 100//表示不可到达
int ctrl[15];
int getDif(int i, int j) {//从i频道up down到j所需次数
int dif = INF;
if (j > i) {
if (ctrl[10]) dif = j-i;
if (ctrl[11] && dif > 50) dif = i+100-j;
} else if (j < i) {
if (ctrl[11]) dif = i-j;
if (ctrl[10] && dif > 50) dif = j+100-i;
} else return 0;
return dif;
}
int main() {
int T, n[333], i, j;
//初始化辅助数组
for (i = 0; i < 301; i++) n[i] = i%100;
scanf("%d", &T);
while(T--) {
int min = INF, count;
//ctrl数组存遥控器
for (i = 0; i < 3; i++) {
for (j = 1; j < 4; j++) scanf("%d", &ctrl[i*3+j]);
scanf("%d", &ctrl[10+i]);
}
scanf("%d", &ctrl[0]);
//其中0-9为数字0-9,10为up,11为down,12为_
//按此格式输人方便后续操作
scanf("%d%d", &i, &j);//i 跳转到 j
min = getDif(i,j);//直接up down
int m = j+101+min;
i = j+100-min;//循环的开始与结尾
if (min == INF) {//min == INF说明up down不可用
m = j+101;
i = j+100;//故调整m,i使循环中只判断能否直接跳转到j
}
for (i; i < m; i++) {
int k = n[i];
count = 0;
if (k < 10) {//中转频道<10时
if (ctrl[k]) {//若该键有用
count++;
count += getDif(k, j);//中转频道再up down
} else count = INF;//该键无用
} else {
if (ctrl[12]) {//_键有用
count++;
int q = k;
while (q) {
if (ctrl[q%10]) count++;
else count += INF;
q /= 10;
}
count += getDif(k, j);
} else count = INF;
}
if (count < min) min = count;
}
if (min >= 100) printf("-1\n");
else printf("%d\n", min);
}
return 0;
}