###### 程序设计实习2016推荐练习3 硬币（dp+数学/位运算压位）

5 18
1 2 3 5 10

2
5 10

k>=0kv[i]<=x(1)kfxkv[i]=0

v[i]y=v[i]yv[i]$含v[i]拼出y的种数=不含v[i]拼出y-v[i]的种数$

version 1

Time Limit Exceeded 2100kB  1160ms  1004 B

#include<stdio.h>
#include<algorithm>

using namespace std;

int n,x,num=0,max_j=0;
int v[200];
bool must[10001][200],legal[10001];
bool first=true;

int main()
{
scanf("%d%d",&n,&x);
for (int i=0;i<n;i++)
scanf("%d",&v[i]);
sort(v,v+n);
legal[0]=true;
for (int i=0;i<n;i++)
for (int j=max_j+v[i]>x?x:max_j+v[i];j>=v[i];j--)
if (legal[j-v[i]])
{
if (legal[j])
{
for (int k=0;k<i;k++)
must[j][k]&=must[j-v[i]][k];
}
else
{
legal[j]=true;
for (int k=0;k<i;k++)
must[j][k]=must[j-v[i]][k];
must[j][i]=true;
}
max_j+=v[i];
}
/*
for (int j=0;j<=x;j++)
{
printf("%d:",j);
for (int i=0;i<n;i++)
if (must[j][i])
printf("%d ",v[i]);
printf("\n");
}
*/
num=0;
for (int i=0;i<n;i++)
if (must[x][i])
num++;
printf("%d\n",num);
for (int i=0;i<n;i++)
if (must[x][i])
if (first)
{
printf("%d",v[i]);
first=false;
}
else
printf(" %d",v[i]);
printf("\n");
return 0;
}

version 2

Accepted    7904kB  430ms   1063 B

#define L sizeof(int)

#include<stdio.h>
#include<algorithm>

using namespace std;

int n,x,num=0,max_j=0;
int v[200];
int must[10000][200],legal[10000];
bool first=true;

inline bool l(int k)
{
return legal[k/L]&(1<<(k%L));
}

inline bool m(int j,int k)
{
return must[j][k/L]&(1<<(k%L));
}

inline void mset(int j,int k,int value)
{
must[j][k/L]|=(value<<(k%L));
}

int main()
{
scanf("%d%d",&n,&x);
for (int i=0;i<n;i++)
scanf("%d",&v[i]);
sort(v,v+n);
legal[0]=1;
for (int i=0;i<n;i++)
for (int j=max_j+v[i]>x?x:max_j+v[i];j>=v[i];j--)
if (l(j-v[i]))
{
if (l(j))
{
for (int k=0;k<=i/L+1;k++)
must[j][k]&=must[j-v[i]][k];
}
else
{
legal[j/L]|=(1<<(j%L));
for (int k=0;k<=i/L+1;k++)
must[j][k]=must[j-v[i]][k];
mset(j,i,1);
}
max_j+=v[i];
}
num=0;
for (int i=0;i<n;i++)
if (m(x,i))
num++;
printf("%d\n",num);
for (int i=0;i<n;i++)
if (m(x,i))
if (first)
{
printf("%d",v[i]);
first=false;
}
else
printf(" %d",v[i]);
printf("\n");
return 0;
} 

version 3

Accepted    256kB   0ms 597 B

#include<stdio.h>
#include<algorithm>

using namespace std;

int n,x,num=0,s,check,v[200],f[10001],must[200];

int main()
{
scanf("%d%d",&n,&x);
for (int i=0;i<n;i++)
scanf("%d",&v[i]);
sort(v,v+n);
f[0]=1;
for (int i=0;i<n;i++)
for (int j=x;j>=v[i];j--)
f[j]+=f[j-v[i]];
num=0;
for (int i=0;i<n;i++)
{
s=1;
check=0;
for (int j=0;j*v[i]<=x;j++)
{
check+=s*f[x-j*v[i]];
s*=-1;
}
if (check==0)
must[num++]=v[i];
}
printf("%d\n",num);
for (int i=0;i<num-1;i++)
printf("%d ",must[i]);
if (num)
printf("%d\n",must[num-1]);
else
printf("\n");
return 0;
} 

#### |算法讨论|状压DP/位运算 学习笔记

2017-02-12 16:27:20

#### ARM预备知识

2017-03-09 18:39:42

#### 程序设计实习之STL专项练习

2013-04-22 18:30:25

#### 2016程序设计实习期末考试总结

2016-06-25 21:13:43

#### 按位运算&数学等价式

2016-12-06 17:13:27

#### 2018《程序设计实习》错误集锦

2018-03-11 11:28:55

#### PKU C++程序设计实习 学习笔记6 标准模板库STL

2015-05-10 12:02:15

#### 位运算与嵌入式编程

2012-05-23 21:01:39

#### 2016程序设计实习期末考试07题上机（dp）

2016-06-26 15:21:28

2013-09-15 11:40:02