1. 程式人生 > >OpenJudge_P4976 硬幣(DP+數論+容斥原理)

OpenJudge_P4976 硬幣(DP+數論+容斥原理)

總時間限制: 1000ms 記憶體限制: 262144kB
描述
宇航員Bob有一天來到火星上,他有收集硬幣的習慣。於是他將火星上所有面值的硬幣都收集起來了,一共有n種,每種只有一個:面值分別為a1,a2… an。 Bob在機場看到了一個特別喜歡的禮物,想買來送給朋友Alice,這個禮物的價格是X元。Bob很想知道為了買這個禮物他的哪些硬幣是必須被使用的,即Bob必須放棄收集好的哪些硬幣種類。飛機場不提供找零,只接受恰好X元。
輸入
第一行包含兩個正整數n和x。(1 <= n <= 200, 1 <= x <= 10000)
第二行從小到大為n個正整數a1, a2, a3 … an (1 <= ai <= x)
輸出
第一行是一個整數,即有多少種硬幣是必須被使用的。
第二行是這些必須使用的硬幣的面值(從小到大排列)。

樣例輸入
5 18
1 2 3 5 10

樣例輸出
2
5 10

提示
輸入資料將保證給定面值的硬幣中至少有一種組合能恰好能夠支付X元。
如果不存在必須被使用的硬幣,則第一行輸出0,第二行輸出空行。

解題思路:我們考慮a[i]是否滿足其實必須元素,容易想到,f[x]-f[x-a[i]]是否為零,但是f[x-a[i]]的方案數中可能也會用到a[i],所以f[x-a[i]]-f[x-a[i]*2],整理一下就是f[x]-f[x-a[i]]+f[x-a[i] *2],也很容易發現容斥規律,由此可以遞迴求解,遞迴邊界為x-a[i] *k<0或者f[x-a[i] *k]==0;
演算法時間複雜度
01揹包O(n*x);遞迴O(n)?//其實不太會算
空間複雜度 nm
ps:絕對原創,表示第一次這麼認真的寫題解233333

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define N 205
#define M 10005
int n,x,l;
int a[N],ans[N];
int f[M];
int calc(int x,int v){
    if(x<0) return 0;
    else return f[x]-calc(x-v,v);
}
int main(){
    scanf("%d%d",&n,&x);
    for(int i=1
;i<=n;i++) scanf("%d",&a[i]); f[0]=1; for(int i=1;i<=n;i++) for(int j=x;j>=a[i];j--) f[j]+=f[j-a[i]]; for(int i=1;i<=n;i++){ if(!(f[x]-calc(x-a[i],a[i]))){ ans[++l]=a[i]; } } printf("%d\n",l); for(int i=1;i<=l;i++) printf("%d ",ans[i]); return 0; }