如何确定我的城市轰炸算法的破坏效率?[关闭]

How to determine the destructive efficiency of my city-bombing algorithm? [closed]

提问人:Jore 提问时间:11/12/2023 最后编辑:Jore 更新时间:11/13/2023 访问量:153

问:


编辑问题以包括所需的行为、特定问题或错误以及重现问题所需的最短代码。这将帮助其他人回答这个问题。

10天前关闭。

这是我想在 C 中解决的问题:

问题

A国和B国之间爆发了一场战争,A国想通过空中轰炸摧毁首都B。但由于战争的突然爆发,双方的弹药储备都很低。由于弹药数量较少,战斗机飞行员杰夫被要求携带摧毁整个城市所需的确切数量的炸弹。每枚炸弹只能投掷到建筑物上。

B国的首都是一个有一维城市,有n座建筑物,其中每座建筑物(i)位于x轴上的某个x(i)处。杰夫必须摧毁城市中的每座建筑物。每枚炸弹都有一个破坏范围 k,这意味着它可以摧毁距离其撞击点 ≤ k 个单位的所有东西。

现在给定了城市地图和炸弹的射程k,杰夫希望你找到他摧毁整个城市所需的最小炸弹数量(每座建筑物必须在至少一枚炸弹的破坏范围内)。

输入

第一行包含两个空格分隔的整数 n(城市中的建筑物数量)和 k(每枚炸弹的破坏范围)。 第二行包含 n 个以空格分隔的整数,表示建筑物在 x 轴上的位置。

输出

打印一个整数,表示摧毁城市所需的最小炸弹数量。

约束

1 ≤ n,k ≤ 10^5
1 ≤ x ≤ 10^5

示例输入

4 2
1 2 3 4

示例输出

1

链接到问题。

问题也可以在以下位置找到:code.dcoder.tech >挑战>硬>耗尽弹药


我的解决方案

#include <stdio.h>
#include <stdlib.h>

/* Variables named according to the problem
 * n         - number of buildings
 * k         - range of bombs
 * buildings - Array containing the integer
 *             coordinates of the buildings
 */
int n, k, *buildings;

void getInput(void);
int isBuilding(int);
int nextBuilding(int);
int lastBuilding(int);

int main(void) {
 getInput();
 int bombs = 0;
 /* Loops through the x cordinates from building 1 to n
  * i - indicates the x coordinate where jeff is
  * j - indicates the bombed area
  * r - indicates the bomb range
  */
 for (int i = buildings[0], j = (buildings[0] - 1), r = k; i <= buildings[n - 1] || j < buildings[n - 1]; i++, r--) {
  /* When joel leaves bomb range coordinates behind */
  if (r == 0) {
   /* Bomb is only dropped on a building */
   while (!isBuilding(i))
    i--;
   /* Increment bombed area (back, position and front)*/
   j = i + k;
   bombs++;
   r = k + 1; // +1 because the loop makes r--
   /* next building beyond range
    * +1 because we need to leave all bombed area behind
    */
   i = nextBuilding(i + k + 1);
   /* if no building */
   if (i == 0)
    break;
   else
    i--; // the loop will make i++
  }
 }
 printf("%d\n", bombs);
 return 0;
}

/* Ascending comparison for qsort */
int compare(const void *a, const void *b) {
 const int *aa = a;
 const int *bb = b;
 return (*aa > *bb) - (*aa < *bb);
}

/* Gets input for n, k and buildings,
 * and sorts buildings
 */
void getInput(void) {
 if (scanf("%d %d", &n, &k) != 2) {
  fprintf(stderr, "input error\n");
  return;
 }
 buildings = calloc(sizeof *buildings, n);
 if (!buildings) {
  fprintf(stderr, "allocation error\n");
  return;
 }
 for (int i = 0; i < n; i++) {
  if (scanf("%d", &buildings[i]) != 1) {
   fprintf(stderr, "input error\n");
   return;
  }
 }
 qsort(buildings, n, sizeof *buildings, compare);
}

/* Returns 1 if it is a building,
 *  0 otherwise
 */
int isBuilding(int p) {
 for (int i = 0; i < n; i++) {
  if (buildings[i] == p)
   return 1;
 }
 return 0;
}

/* Returns position of next building greater or equal to p,
 * 0 if none available
 */
int nextBuilding(int p) {
 for (int i = 0; i < n; i++) {
  if (buildings[i] >= p)
   return buildings[i];
 }
 return 0;
}

/* Returns position of last building less or equal to p
 * 0 if none available
 */
int lastBuilding(int p) {
 for (int i = 0; i < n; i++) {
  if (buildings[i] == p)
   return buildings[i];
  else if (buildings[i] > p)
   return buildings[--i];
 }
 return 0;
}

解释

我采取更直接的方法解决这个问题,因为试图找到一个公式似乎是不可能的。

我主要使用杰夫的位置,他向前移动(炸弹射程)距离,然后在那里放置炸弹,如果当时有建筑物,或者回到最后一栋建筑物。(这个问题明确提到炸弹只能放置在建筑物上)。ik

然后我移动到下一个未被轰炸的建筑物并重复上述过程。i

条件是当有剩余的建筑物但无法满足(已超过最后一栋建筑物)时。j < ni <= buildings[n - 1]i

问题

代码在我这边似乎运行良好。

除了问题中的输入之外,我还测试了各种输入(如下所示),并得到了所需的输出。

Input:
6 2
1 2 3 4 5 6
Output: 2

Input:
10 2
2 3 8 16 17 18 19 20 21 22
Output: 4

尽管当我提交问题时,所有测试都失败了

我哪里出错了?

C 算法

评论

0赞 BoP 11/12/2023
buildings指向 中的局部数组。当函数返回时,该数组超出范围。tempgetInput
0赞 Jore 11/12/2023
@BoP感谢您的更正,但似乎无法解决问题。所有测试仍然失败。我已经更新了问题(使用新的解决方案)。
0赞 n. m. could be an AI 11/12/2023
永远不要使用全局变量。
0赞 Jore 11/12/2023
@n.m.couldbeanAI,我编辑了问题并添加了新代码。它在顶部标记了更新。
1赞 halfer 11/12/2023
“测试失败”不足以描述此页面上的验收标准。您能更具体地介绍一下成功的解决方案是什么样子吗?

答:

1赞 chqrlie 11/12/2023 #1

以下是一些问题:

  • 在函数中,将指向局部数组的指针存储到全局变量 : 中。getInput()tempbuildingsbuildings = temp;

    从 after returns 指向的数组中读取具有未定义的行为。您应该改用 或 来分配内存。buildinggetInput()malloccalloc

  • 此外,您还假设建筑物偏移是按升序输入的,但分配中没有表示此类约束。您可能希望在输入期间或之后对数组进行排序。

  • isBuilding(0)在所有情况下都返回,因此无法测试是否存在偏移处的建筑物。如果验证了约束,这应该不是问题,但为了保持一致性,如果发现建筑物具有偏移量,则应返回。00x >= 1isBuilding(p)1p

这是修改后的版本:

#include <stdio.h>
#include <stdlib.h>

int compare_ints(const void *a, const void *b) {
    const int *aa = a;
    const int *bb = b;
    return (*aa > *bb) - (*aa < *bb);
}

/* Gets input for n, k and buildings */
void getInput(void) {
    if (scanf("%d %d", &n, &k) != 2) {
        fprintf(stderr, "input error\n");
        return;
    }
    buildings = calloc(sizeof *buildings, n);
    if (!buildings) {
        fprintf(stderr, "allocation error\n");
        return;
    }
    for (int i = 0; i < n; i++) {
        if (scanf("%d", &buildings[i]) != 1) {
            fprintf(stderr, "input error\n");
            return;
        }
    }
    qsort(buildings, n, sizeof *buildings, compare_ints);
}

/* Returns 1 if it is a building, 0 otherwise */
int isBuilding(int p) {
    for (int i = 0; i < n; i++) {
        if (buildings[i] == p)
            return 1;
    }
    return 0;
}

评论

0赞 Jore 11/12/2023
我已经相应地添加了代码。这似乎没有解决问题。出于某种原因,所有的测试都失败了,但我自己的测试通过了。谢谢
0赞 n. m. could be an AI 11/12/2023
@Jore您的代码仍然没有对输入进行排序。
0赞 Jore 11/12/2023
@n.m.couldbeanAI,现在你说到它。可以对输入进行排序。让我试试
0赞 chqrlie 11/12/2023
@Jore:我用高效的排序代码更新了答案。
1赞 Jore 11/13/2023
事实证明,这是我提交的网站中的一个错误,它对 C 来说效果不佳,用 java 重新编写解决方案解决了这个问题
1赞 n. m. could be an AI 11/12/2023 #2

编辑:
我错过了一个重要的条件,即炸弹只能投在建筑物上。它使这个答案的简单化方法无效。

编辑2
:在第二点上,条件的问题似乎没有太大不同。下面是一个更新和注释的主循环:

qsort(buildings, n, sizeof(*buildings), compare_int);
int n_bombs = 0;
for (int i = 0; i < n;)
{
    // What is the first building that if we drop on it
    // we won't destroy this building?
    int drop = i;
    while (drop < n && buildings[drop] - buildings[i] <= k) 
      ++drop;
    // Back up by one building and drop a bomb on it
    --drop;
    ++n_bombs;

    // What is the first building that was not destroyed?
    int j = drop;
    while (j < n && buildings[j] - buildings[drop] <= k)
      ++j;
    
    // It will be our next building to consider
    i = j;
}

它根据要求计算 2、3、4、7、10、11、12、13、14、15 的 4 枚炸弹。没有进一步测试。

此循环具有一些重复代码,因此函数是有序的。如果我们介绍:nextBuilding

int nextBuilding (int* buildings, int current, int radius, int total)
{
    int next = current;
    while (next < total && buildings[next] - buildings[current] <= radius)
      ++next;
    --next;
    return next;
}

循环压缩为

for (int i = 0; i < n;)
{
    int drop = nextBuilding(buildings, i, k, n);
    ++n_bombs;
    i = nextBuilding(buildings, drop, k, n) + 1;
}

原始无效答案如下
你在主循环中的逻辑很难遵循。这就是它的样子,假设有排序的输入(警告,未经测试的代码)。

qsort(buildings, n, sizeof(*buildings), compare_int);
int n_bombs = 0;
for (int i = 0; i < n;)
{
  ++n_bombs;
  int last_destroyed_x = buildings[i] + 2*k;  // edit: was 2*k + 1
  while (i < n && buildings[i] <= last_destroyed_x)
    ++i;
}

不需要像 或 这样的辅助函数。isBuildingnextBuilding

评论

0赞 Jore 11/12/2023
让我试试这个解决方案。谢谢
0赞 Jore 11/12/2023
此外,我还为问题中的代码添加了注释。我使用更直接的方法,其中 joel 移动并投下炸弹作为 i 增加。i
0赞 Jore 11/12/2023
这个解决方案不起作用,我认为这就是原因。您的代码假定每个坐标都有一个建筑物。(不对)
0赞 Jore 11/12/2023
假设像 _ 2 3 4 _ _ 7 _ _ 10 11 12 13 14 15 这样的 somerhing,其中破折号是空坐标,范围 (k) 2 的答案是 4,即放置在 4、7、12 和 15 处的炸弹
0赞 n. m. could be an AI 11/12/2023
“您的代码假定每个坐标都有一个建筑物”不,它没有。我没有测试此代码,因此它可能会或可能不会起作用,但与它非常相似的东西应该可以起作用。