CF632E Thief in a Shop

题目链接:http://codeforces.com/contest/632/problem/E

题目大意:

  从 \(n\) 个不同的数中取 \(k\) 个(可重复取)并加起来,问最多能得到多少种不同的和,从小到大输出所有的和。

  (\(1 \le n,k,a_i \le 1000\))

知识点:  DP

解题思路:

  首先明确一点:对于 \(5s\) 的时限,\(O(1e9)\) 的复杂度是可以过的,因此我们可以来考虑 \(DP\).

  具体的方法是:先把 \(a[]\) 数组从小到大排序,将 \(a[0]\) 视为 \(0\),即零点上移 \(a[0]\),则 \(a[]\) 数组中其他的数也要相应地减掉 \(a[0]\)。对这个零点上移 的数组做 \(dp\),使得对每一个和所用的数的个数尽可能少,最后打印答案的时候再将零点复位,则对于需要数的个数小于等于 \(k\) 的和我们都是可以用 \(a[]\) 数组中的 \(k\) 个数取到的(此时 \(0\) 又变回了 \(a[0]\))。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1004,inf=0x3f3f3f3f;
 4 
 5 int dp[maxn*maxn];
 6 int a[maxn],da[maxn];
 7 
 8 int main(){
 9     int n,k;
10     scanf("%d%d",&n,&k);
11     for(int i=0;i<=1000*k;i++)  dp[i]=inf;
12     for(int i=0;i<n;i++)    scanf("%d",&a[i]);
13     sort(a,a+n);
14 
15     for(int i=1;i<n;i++)
16         da[i]=a[i]-a[0];
17     dp[0]=0;
18     for(int i=1;i<n;i++){
19         for(int j=0;j<1000*k;j++){
20             if(dp[j]<inf)
21                 dp[j+da[i]]=min(dp[j+da[i]],dp[j]+1);
22         }
23     }
24     
25     for(int i=0;i<=1000*k;i++){
26         if(dp[i]<=k)
27             printf("%d ",i+k*a[0]);
28     }printf("\n");
29     
30     return 0;
31 }

 

posted @ 2018-05-22 12:20  Blogggggg  阅读(177)  评论(0编辑  收藏  举报