📌  相关文章
📜  三星半导体研究所(SSIR软件)实习生/FTE |组 2

📅  最后修改于: 2021-11-10 07:01:23             🧑  作者: Mango

有 N 个标有 Bi 值的气球(其中 B(i…N))。
用户将获得带有 N 颗子弹的枪,用户必须射击 N 次。
当任何气球爆炸时,其相邻的气球就会彼此相邻。
用户必须获得最高分才能获得奖品,分数从 0 开始。
以下是计算分数的条件。
当气球 Bi 爆炸时,分数将是 Bi-1 和 Bi+1 的乘积(分数 = Bi-1 * Bi+1)。
当 Balloon Bi 爆炸并且只剩下气球时,得分将为 Bi-1。
当气球 Bi 爆炸并且只有正确的气球存在时,得分将为 Bi+1。
当气球Bi爆炸并且没有左右气球存在时,得分将为Bi。
编写一个程序来获得最高分。

Example: 
Input: B[] = {1, 2, 3, 4}
Output: 20
Explanation:
For max score:
3 explodes, score= 4*2=8 (product of adjacent balloons) 
2 explodes score= 4*1 + 8 = 12 (product of adjacent balloons) 
1 explodes score=  4 + 12= 16  (only 4 is left on the left side)
4 explodes score = 4 + 16 = 20 (no balloons left so add 4)
score =20
other combinations will result in lesser scores.

分析:
1)目标是找到最高分
2)最大分数取决于邻居的分数,但是没有简单的方法可以找到哪个序列给出最大分数,所以唯一的方法是找到所有可能的序列可以从中获得最大分数。
3)由于输入N的顺序很重要,我们可以有N!序列,即。 nPn 路(第一个气球 N 路,第二个 N-1 路……最后一个气球 1 路 N*(N-1)(N-2)..2*1= N!

复杂:
生成所有序列 O(N!)
要获得 1 个序列的分数,对于序列中的每个气球,我们需要左右邻居最坏的情况需要完全遍历数组,因此复杂度为 O(N*N)
总复杂度为 O(N!) * O(N*N)(注意:计算已在每个序列的末尾完成)
50 TC,N 50 * 是 O(N! * N*N) => 50 * 100 * 10! => 5000 * 3628800 => 1.5 * 10^10 这不能在给定的 3 秒内执行(每秒 10^9 条指令)。
所以需要寻找优化

生成所有序列的伪代码:

INPUT[N]
CHOICE[N] <= -1 //initialize to -1
Permute(0)
Permute(Position)
{
   // stop condition
   If ( all balloon shot )
   {
     Compute the score for this sequence in CHOICE[]
     If score better than previous then store
   }
 
   For i:0~N-1
   {
     If (ith balloon not selected // CHOICE[i]==-1)
     {
        Select ith balloon  // CHOICE[Position]= i 
        Permute (Position+1)
        Unselect ith balloon// CHOICE[Position]= -1
     }
   }
}

优化:
我们可以看到在上面的算法中进行了2个主要操作
(1) 生成所有序列 O(N!)
(2) 计算每个序列的分数 O(N*N)
我们无法优化算法生成所有序列,但是我们可以进一步减少计算部分。
优化计算部分
如果可以优化寻找邻居的 O(1) 我们可以将计算部分减少到 O(N) 这导致我们的算法在 1.5 * 10^9 中执行,这可以在 3 秒内实现。
或者,我们可以计算每个选择的气球的分数,以便“在旅途中”拍摄,这里每次选择气球时寻找邻居是额外的,这可以是 O(N) 并且还减少 1.5 * 10^9

获取邻居的算法:
O(N) 的朴素方法:
邻居(选择)
对于左侧:如果左侧气球未选择中断,则选择-1~0;
For Right: 选择+1~N-1 如果没有选择右边的气球破裂;
如果(对== N)
右=-1;
左右返回;

O(1) 的优化方式
1.保留 2 个数组 left[] 和 right[],其中包含每个气球的邻居。
2.初始邻居是已知的,因为第i个气球左边是i-1,右边是i+1,除了第一个气球没有左边,最后一个气球没有右边。
3.当气球被选中时,我们可以通过O(1)获得它的左右
4.当气球被击中时更新邻居left[i+1]=left[i] right[i-1]=right[i]
笔记:
而不是调用新函数来获得左右计算在递归派中左右计算会减少许多隐藏指令,因为调用新函数编译器添加了许多可以减少的指令

替代方式:
随时随地计算分数的方法
将当前分数变量传递给递归函数
当一个气球被选择拍摄时,得到左右邻居
计算通过射击所选气球获得的分数
将此添加到给定的分数并传递到下一个级别
伪代码:

Permute(Position, score)
{
   // stop condition
   If( all balloon shot )
   {
     If score better than previous then store
   }
 
   For i:0~N-1
   {
      If (ith balloon not selected // CHOICE[i]==-1)
      {
         Select ith balloon  // CHOICE[Position]= i 
         Gain = Compute the gain by shooting ith balloon 
         Permute (Position+1, score+ Gain)
         Unselect ith balloon// CHOICE[Position]= -1
      }
   }
} 

替代优化方法(分而治之)和动态规划

这个问题起初看起来不像是一个分而治之的问题。
原因:如果我们选择一个气球(用于爆破),那么我们的阵列将被分成两个子阵列。但这两个子阵列不会是独立的子问题。
例子
考虑 5 个气球 B1、..、B5。突发 B3 将数组分成两个子数组 {B1, B2} 和 {B4, B5}。但是这两个子数组不是相互独立的,即。突发 B4 的分数取决于突发顺序 {B1, B2}。

关键洞察
1. 将问题分成两半,我们必须确保一半的任何动作(气球爆裂)不会影响另一半的得分。
2.如果我们修复一个气球,并确保在我们把左边的所有气球和右边的所有气球都爆破之前我们不会爆破它,那么我们可以成功地将问题分解为两个子问题。

例子
考虑前面五个气球的情况。现在,我们不再爆破 B3,而是在所有气球之后爆破 B3,这使得 {B1, B2} 和 {B4, B5} 相互独立,即爆破 B4 的分数现在独立于 {B1, B2}。
桌子

将分而治之的方法形象化的另一种方法是我们反向思考问题。平行问题将给出一组 n 个放气的气球,每个气球都有一个分数,选择你将气球充气的顺序。给气球充气的分数等于附加在上述气球左侧和右侧的气球上的分数的乘积。

下面是递归解决方案:

#include 
using namespace std;
  
// recursive function to generate scores
int getmaxscore(int arr[], int l, int r, int n)
{
    int mscore = 0;
    for (int i = l + 1; i < r; i++) {
  
        // to permute through all cases
        int cs = getmaxscore(arr, l, i, n) + getmaxscore(arr, i, r, n);
        if (l == 0 && r == n)
            cs = cs + arr[i];
        else
            cs = cs + (arr[l] * arr[r]);
  
        if (cs > mscore)
            mscore = cs;
    }
    return mscore;
}
  
int main() // driver function
{
    int n = 4; // no of balloons
  
    // assigning scores to each balloon 1-based indexing
    // arr[0]=1 because to calculate score when no 
    //             balloons are left after popping
    // arr[5]=1 because to calculate score when no
    //             balloons are left after popping
    // scores of balloons are assigned from 1 to 4 i.e 1 to n
    int arr[] = { 1, 1, 2, 3, 4, 1 }; 
  
    /* for input input arr[n+2], 
      arr[0]=1 && arr[n+1]=1
      cin>>n;
      for(int i=1;i<=n;i++)
         cin>>arr[i]; */
  
    cout << getmaxscore(arr, 0, n + 1, n + 1) << "\n";
  
    return 0;
}
Output:
20