用于均衡数字列表的最小移动次数

时间:2015-01-01 04:42:14

标签: c++ algorithm dynamic-programming

我们有一个n个正整数的数组。可接受的移动是将所有元素增加1或2或5,除了一个元素。我们需要找出最小数量的操作,以使所有数组元素的数量相等。

搜索后我发现了一种方法:

  1. 找出非最小元素与最小元素之间的所有差异。
  2. 使用硬币更改方法,找出所有差异所需的最小硬币数量。
  3. 返还所有这些最小硬币数量的总和。
  4. 但是这种方法在这个测试用例中失败了:

    数组:1,5,5

    最少操作次数:3(1,5,5 - > 1,6,6 - > 6,6,11 - > 11,11,11)。

    按照上述方法,我得到了4。

    有人能提出正确的方法吗?

    这是我的源代码:

    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <climits>
    using namespace std;
    
    int input[10000];
    int input1[10000];
    int dp[4][1001];
    int parent[4][1001];
    int coins[4]={0,1,2,5};
    int operation=0;
    int main() {
        int t,n,i,j,count,sum,diff,prevdiff,min;
        for(i=1;i<1001;i++)
        {
            dp[0][i]=INT_MAX;
            parent[0][i]=INT_MAX;
        }
        for(i=0;i<4;i++)
        {
            dp[i][0]=0;
            parent[i][0]=-1;
        }
        for(i=1;i<1001;i++){
            for(j=1;j<4;j++){
                dp[j][i]=dp[j-1][i];
                parent[j][i]=parent[j-1][i];
                if(i>=coins[j]&&dp[3][i-coins[j]]<INT_MAX){
    
                    if(dp[3][i-coins[j]]+1<dp[j][i]){
                        dp[j][i]=dp[3][i-coins[j]]+1;
                        parent[j][i]=j;
                    }
                }
            }
        }
        cin>>t;
        while(t>0){
            cin>>n;
            min=INT_MAX;
            for(i=0;i<n;i++)
            {
                cin>>input[i];
                if(input[i]<min){
                    min=input[i];
                }
                //input1[i]=input[i];
            }
    
            //sort(input,input+n);
    
            count=0;
            sum=0;
    
            for(i=0;i<n;i++){
                count=count+dp[3][input[i]-min];
            }
    
            cout<<count<<endl;
            t--;
        }
        /*
        for(i=1;i<1001;i++){
            if(dp[3][i]!=minCoins(i))
                cout<<dp[3][i]<<" "<<minCoins(i)<<endl;
        }
        */
        return 0;
    }
    

2 个答案:

答案 0 :(得分:4)

您发现的方法不适用于由1,2和5组成的元素集。正如您所说,对于1, 5, 5,该方法会产生4个操作(对于&#34)硬币改变&#34;),例如:

1, 5, 5 - &gt; 1, 3, 5 - &gt; 1, 1, 5 - &gt; 1, 1, 3 - &gt; 1, 1, 1

为了均衡所有元素,将除1个元素之外的所有元素增加1,2或5,与将一个元素减少相应的值基本相同(参见this answer)。如果您以这种方式查看问题,那么它等于this问题。

后一问题的answer解释了仅考虑最小元素和非最小元素之间的差异是不够的。您还必须考虑所有元素与最小元素的差异 - 1和最小元素 - 2.对于1, 5, 5,这会导致例如以下操作:

1, 5, 5 - &gt; 0, 5, 5 - &gt; 0, 0, 5 - &gt; 0, 0, 0

1, 5, 5 - &gt; -1, 5, 5 - &gt; -1, 0, 5 - &gt; -1, -1, 5 - &gt; -1, -1, 0 - &gt; -1, -1, -1

正如您所看到的,对于您的示例,考虑所有元素和最小元素之间的差异 - 1给出了均衡所有元素所需的最小操作数,即3。

您应该调整代码以反映这种方法。

答案 1 :(得分:1)

将一个数字除以1,2或5之外的所有数字都等于将该数字除以1,2或5的相同。因此,此问题可以转换为另一个问题,其中-

我们希望仅使用1个操作来使所有数字相等,即将特定数字减少1,2或5。

为解决此问题,我们可以在数组中找到最小数目。所有数字的最终值将为[min(Array)-4, min(Array)]。我们可以迭代所有5个值,对于每个值,我们可以找到使所有元素均达到所选值的最小移动次数。最后,在每个测试用例中获取所有5个答案中的最小值。那将是结果。这是我的C ++代码-

#include<bits/stdc++.h>
using namespace std;

#define int long long int

signed main(){
    int t;
    cin>>t;
    while(t--){
        int n, res = INT_MAX, mini = INT_MAX, ans, temp;
        cin>>n;
        int A[n];
        for(int i=0;i<n;i++){
            cin>>A[i];
            mini = min(mini, A[i]);
        }
        for(int i=mini-4;i<=mini;i++){
            ans = 0;
            for(int j=0;j<n;j++){
                temp = A[j]-i;
                temp = temp/5+(temp%5)/2+(temp%5)%2;
                ans += temp;
            }
            res = min(ans, res);
        }
        cout<<res<<"\n";
    }

    return 0;
}