【问题标题】:Number of ways to distribute N items to three peoples under given conditions在给定条件下将 N 个物品分配给三个人的方法数
【发布时间】:2021-03-14 15:01:54
【问题描述】:

我一直在尝试解决一个问题,即确定将 N 个物品分发给三个人的方法的数量。如果第一个人得到的物品的总重量可以被 3 整除,第二个人得到的物品的重量可以被 5 整除,最后一个人得到的物品的总重量可以被 7 整除,那么分配将被认为是最好的。每个人得到的物品不应该是0。

第一个输入 N 被认为是项目数,第二行输入表示 N 个整数,表示项目的权重。第 i 个整数 a[i] 表示第 i 个项目的权重。有这样的限制,

3≤ N ≤12
1≤ a[i] ≤10^10

sample input 1:
6
2 3 5 7 9 13

sample output 1:
6

我尝试了一种解决问题的方法,但无法弄清楚如何更好地解决问题,以便第一人、第二人和第三人收到的物品的重量分别被 3、5 和 7 整除.我尝试让一个人在三个人中获得最大数量的物品,每个人至少获得一件物品,但无法弄清楚每个人获得的物品总重量可以被 3 整除的“最佳”解决方案, 5 和 7 分别。 您能否建议 DP 中的解决方案来解决所有测试用例中的问题?

这是我在三个人之间进行简单分配的解决方案。 TIA。

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

int countWays(int N)
{
    int ans = ((N-1)*(N-2))/2;

    //storing the number of distribution that is not possible
    int s = 0;

    for(int i=2; i<=N-3; i++){
        for(int j=1; j<i; j++){
            //possibilities of 2 persons receiving the maximum
            if (N == 2 * i + j)
                s++;
        }
    }

    if(N%3 == 0)
        s = 3*s+1;
    else
        s = 3*s;

    //final ways to distribute
    return ans - s;
}

int main()
{
    int n, N=0;
    cin >> n;

    if(n<3 && n>12)
        return 0;

    vector<int> a(n);

    for(int i=0; i<n; i++){
        cin>>a[i];
        N += a[i];
    }

    cout<<countWays(N);

    return 0;
}

【问题讨论】:

    标签: c++ algorithm c++11 dynamic-programming


    【解决方案1】:

    您需要的动态编程状态是表示人 1、2 和 3 是否收到任何东西的三个标志,然后是 3 个数字,表示他们收到 mod 3、5 和 7 的总和。

    在 Python 中,我会构建一个字典,其键是表示状态的元组,其值是计数。在 C++ 中,您可能更喜欢将状态表示为一个整数(每个值都由特定位编码)和一个表示查找的数组。无论哪种方式,您都从一端开始计数 1,因为没有人收到任何东西,处理每个项目并跟踪计数,以了解如何将它们分配给每个人,然后在另一端查找每个人都收到东西的计数总数为0。

    这是一个 Python 解决方案,您可以验证它是否有意义。

    def divisions (item_weights):
        count_by_state = {(False, 0, False, 0, False, 0): 1}
        for weight in item_weights:
            next_count_by_state = {}
    
            for state, count in count_by_state.items():
                # Each variable is pX_Y where
                #   X is the person
                #   Y is either r for whether they [r]eceived or w for [w]eight.
                p1_r, p1_w, p2_g, p2_w, p3_g, p3_w= state
                for next_state in [
                    (True, (p1_w + weight)%3, p2_g, p2_w, p3_g, p3_w),
                    (p1_r, p1_w, True, (p2_w + weight)%5, p3_g, p3_w),
                    (p1_r, p1_w, p2_g, p2_w, True, (p3_w + weight)%7),
                    ]:
                    if next_state in next_count_by_state:
                        next_count_by_state[next_state] += count
                    else:
                        next_count_by_state[next_state] = count
    
            count_by_state = next_count_by_state
        return count_by_state[(True, 0, True, 0, True, 0)]
    
    print(divisions([2, 3, 5, 7, 9, 13]))
    

    注意,通过更多的工作,我实际上可以按字典顺序打印解决方案。如果有更多,我可能会被要求提供第 500 个,然后只打印那个。然而,这是我已经解决的动态编程技巧,这些比赛似乎都不重要。

    【讨论】:

      【解决方案2】:

      对于这么小的项目数量,您只需生成所有项目分布(3^12~~531000)并检查条件。

      int countWays(int n, int* arr, int aa = 0, int bb = 0, int cc = 0) {
          if (n < 0) {
              return (aa*bb*cc > 0 && (aa % 3) == 0 && (bb % 5) == 0 && (cc % 7) == 0) ? 1 : 0;
          }
          return countWays(n - 1, arr, aa + arr[n], bb, cc) +
                 countWays(n - 1, arr, aa, bb + arr[n], cc) +
                 countWays(n - 1, arr, aa, bb, cc + arr[n]);
      }
      
      int main()
      {
          int n = 6;
          int a[6] = { 2, 3, 5, 7, 9, 13 };
      
          std::cout << countWays(n-1, a);
      }
      

      打印6

      对于较大的项目计数,也许值得使用带有记忆的动态编程。

      【讨论】:

      • 对于给定的限制程序以毫秒为单位工作,但 DP 要复杂得多(至少我现在认为)
      • 我对 DP 的原始想法是:对于数组切片 a[0..k-1] 制作一个表格,其中的和数为模 3:0、1、2;按模 5:0..4;按模 7:0..6;并使用项目 a[k] 更新表。继续 k+1...
      • 没有详细说明。我想到了一张有 105 个条目的表。还要考虑中间相遇原则:像我的代码一样用蛮力计算左边 n/2 项的表格,计算右边 n/2 项的表格。将两个表中的计数相乘以获得互补条目并将它们相加。适用于n~30-34
      • 动态编程应该不会很复杂。
      • 我用 Python 代码添加了一个 DP 解决方案。这不是很复杂。为了好玩,我向它扔了前 1000 个方块,它在大约三分之一秒内产生了 475 位数字的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-16
      相关资源
      最近更新 更多