PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 T283097 硬币
题目
题目描述
你有 n n n 个硬币,第 i i i 个硬币面值为 a i a_i ai,现在总队长想知道如果丢掉了某个硬币,剩下的硬币能组成多少种价值?( 0 0 0 价值不算)
输入格式
第一行一个整数 n n n
第二行 n n n 个整数: a 1 , a 2 ⋯ a n a_1,a_2\cdots a_n a1,a2⋯an。
输出格式
输出 n n n 行,第 i i i 行表示没有第 i i i 个硬币能组成多少种价值。
样例 #1
样例输入 #1
3
1 1 3
样例输出 #1
3
3
2
提示
对于 30 % 30\% 30% 的数据 1 ≤ n ≤ 50 , 1 ≤ a i ≤ 100 1\le n\le50,1\le a_i\le100 1≤n≤50,1≤ai≤100;
对于 100 % 100\% 100% 的数据 1 ≤ n ≤ 100 , 1 ≤ a i ≤ 3000 1\le n \le 100,1 \le a_i \le3000 1≤n≤100,1≤ai≤3000。
题解
背包DP
转移方程:
t
[
j
+
a
[
i
]
]
+
=
t
[
j
]
t[j+a[i]]+=t[j]
t[j+a[i]]+=t[j]
注意要从大到小转移,避免后效性。
统计
先减去产生的影响(从小到大),统计非零的个数,再把影响加回去(从大到小)
代码实现
//洛谷 T283097 硬币
#pragma GCC optimize(3)
#include<cstdio>
#include<iostream>
using namespace std;
int n;
int a[110];
int t[300010];
int sum;
int ans;
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
int main(){
register int i,j,k;
in(n);
t[0]=1;
for(i=1;i<=n;++i){
in(a[i]);
for(j=sum;j>=0;--j){
if(t[j]){
t[j+a[i]]+=t[j];
}
}
sum+=a[i];
}
for(i=1;i<=n;++i){
ans=0;
for(j=0;j<=sum-a[i];++j){
if(t[j]){
t[j+a[i]]-=t[j];
}
}
for(j=1;j<=sum;++j){
if(t[j]){
++ans;
}
}
for(j=sum-a[i];j>=0;--j){
if(t[j]){
t[j+a[i]]+=t[j];
}
}
printf("%d\n",ans);
}
return 0;
}