USACO section 2.2 Subset Sums(DP,背包)

53 篇文章 0 订阅
  
  
Subset Sums JRM

For many sets of consecutive integers from 1 through N (1 <= N <= 39), one can partition the set into two sets whose sums are identical.

For example, if N=3, one can partition the set {1, 2, 3} in one way so that the sums of both subsets are identical:

  • {3} and {1,2}

This counts as a single partitioning (i.e., reversing the order counts as the same partitioning and thus does not increase the count of partitions).

If N=7, there are four ways to partition the set {1, 2, 3, ... 7} so that each partition has the same sum:

  • {1,6,7} and {2,3,4,5}
  • {2,5,7} and {1,3,4,6}
  • {3,4,7} and {1,2,5,6}
  • {1,2,4,7} and {3,5,6}

Given N, your program should print the number of ways a set containing the integers from 1 through N can be partitioned into two sets whose sums are identical. Print 0 if there are no such ways.

Your program must calculate the answer, not look it up from a table.

PROGRAM NAME: subset

INPUT FORMAT

The input file contains a single line with a single integer representing N, as above.

SAMPLE INPUT (file subset.in)

7

OUTPUT FORMAT

The output file contains a single line with a single integer that tells how many same-sum partitions can be made from the set {1, 2, ..., N}. The output file should contain 0 if there are no ways to make a same-sum partition.

SAMPLE OUTPUT (file subset.out)

4

 
 
具体看程序:
 
 
/*
ID:nealgav1
PROG:subset
LANG:C++
*/
#include<fstream>
#include<cstring>
using namespace std;
const int mm=2300;
long long f[60][mm];
long long dp(int x,int c)
{ memset(f,0,sizeof(f));
  f[1][0]=1;f[1][1]=1;
  for(int i=2;i<=x;i++)
  for(int j=0;j<=c;j++)
  if(j-i>=0)
  f[i][j]=f[i-1][j]+f[i-1][j-i];
  else f[i][j]=f[i-1][j];
  /** 容量为j,个数为i的方法数有两种情况,一是个数为i-1,容量就已经是j了,这时不用
  往里面放东西,另一种是当个数为i-1,容量为j-i;这时只要将个数i放入容量即为j;因此
  f[i][j]=f[i-1][j]+f[i-1][j-i];*/
  return f[x][c];
}
int main()
{ ifstream cin("subset.in");
  ofstream cout("subset.out");
  int m,ans;
  cin>>m;
  ans=((1+m)*m)/2;
  if(ans&1){cout<<"0\n";return 0;}///当和为奇数时不可能
  else ans/=2;
  cout<<dp(m,ans)/2<<"\n";///答案是一半和容量的1/2.
}

 
 
 
 
 
USER: Neal Gavin Gavin [nealgav1]
TASK: subset
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 4412 KB]
   Test 2: TEST OK [0.000 secs, 4412 KB]
   Test 3: TEST OK [0.011 secs, 4412 KB]
   Test 4: TEST OK [0.011 secs, 4412 KB]
   Test 5: TEST OK [0.011 secs, 4412 KB]
   Test 6: TEST OK [0.011 secs, 4412 KB]
   Test 7: TEST OK [0.000 secs, 4412 KB]

All tests OK.

YOUR PROGRAM ('subset') WORKED FIRST TIME! That's fantastic -- and a rare thing. Please accept these special automated congratulations.

Here are the test data inputs:

------- test 1 ----
7
------- test 2 ----
15
------- test 3 ----
24
------- test 4 ----
31
------- test 5 ----
36
------- test 6 ----
39
------- test 7 ----
37
Keep up the good work!

 

Thanks for your submission!

 

Subset SumsRob Kolstad

This is a classic dynamic programming problem. Hal's solution is shown below.

/* Calculate how many two-way partitions of {1, 2, ..., N} are
   even splits (the sums of the elements of both partition are equal) */

#include <stdio.h>
#include <string.h>

#define MAXSUM 637

unsigned int numsets[637][51];

int max;
unsigned int sum;

main(int argc, char **argv)
{
  int lv, lv2, lv3;
  int cnt;
  FILE *fin, *fout;

  fin = fopen ("subset.in", "r");
  fscanf(fin, "%d", &max);
  fclose (fin);
  fout = fopen("subset.out", "w");

  if ((max % 4) == 1 || (max % 4) == 2) {
    fprintf (stderr, "0\n");
    exit(1);
  }

  sum = max * (max+1) / 4;

    memset(numsets, 0, sizeof(numsets[0]));
    numsets[0][0] = 1;
    for (lv = 1; lv < max; lv++) {
      for (lv2 = 0; lv2 <= sum; lv2++)
        numsets[lv2][lv] = numsets[lv2][lv-1];
      for (lv2 = 0; lv2 <= sum-lv; lv2++)
        numsets[lv2+lv][lv] += numsets[lv2][lv-1];
    }

    fprintf (fout, "%u\n", numsets[sum][max-1]);
    fclose (fout);
  exit (0);
}

and here's an even more concise solution from Nick Tomitov of Bulgaria:

#include <fstream>
using namespace std;
const unsigned int MAX_SUM = 1024;
int n;
unsigned long long int dyn[MAX_SUM];
ifstream fin ("subset.in");
ofstream fout ("subset.out");

int main() {
    fin >> n;
    fin.close();
    int s = n*(n+1);
    if (s % 4) {
        fout << 0 << endl;
        fout.close ();
        return ;
    }
    s /= 4;
    int i, j;
    dyn [0] = 1;
    for (i = 1; i <= n; i++)
        for (j = s; j >= i; j--) 
            dyn[j] += dyn[j-i];
    fout << (dyn[s]/2) << endl;
    fout.close();
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值