bzoj3233 找硬币 动态规划&线性筛

    这道题目居然是用动态规划,我还以为是搜索呢。。。不过据说有人找到了搜索的题解???反正我是找不到。

    废话少说。不过这道题目的动态规划的性质还是比较明显的。因为对于一个硬币的集合S(假设S中的数已经排序),因为S中相邻的数成整除关系,所以显然是先把最大的用掉,再用第二大的,以此类推。例外,如果有S'∈S,显然S比S'更优,或者说,不更差。

    那么,我们得到下面的转移方程,设f[x]表示当x是最大值时剩余部分(就是先把i用掉以后没一只兔纸还需要的价格,即{a[i]%x})的最优解,那么:

    f[x]=min{f[j]+w(x,j)},当且仅当j|x且(x/j)为质数(后者虽然不是必要条件,但是充分条件,如果去掉就会Tle)。其中w(x,j)=Σ(i=1,n)a[i]%x/j,即把x用掉后需要用多少j。

    最后,ans=min{f[i]+w(+∞,i)}。另外,实现时用刷表法比较方便。

AC代码如下:

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#define inf 1000000000
using namespace std;

int n,m,cnt,a[105],c[30005],f[100005]; bool vis[100005];
int work(int x,int y){
	int i,sum=0; for (i=1; i<=n; i++) sum+=a[i]%y/x; return sum;
}
int main(){
	scanf("%d",&n); int i,j,ans=inf;
	for (i=1; i<=n; i++){
		scanf("%d",&a[i]); m=max(m,a[i]);
	}
	for (i=2; i<=m; i++){
		if (!vis[i]) c[++cnt]=i;
		for (j=1; j<=cnt && i*c[j]<=m; j++){
			vis[i*c[j]]=1; if (!(i%c[j])) break;
		}
	}
	for (i=2; i<=m; i++) f[i]=inf; ans=inf;
	for (i=1; i<=m; i++){
		ans=min(ans,f[i]+work(i,inf));
		for (j=1; j<=cnt; j++){
			int k=i*c[j]; if (k>m) break;
			f[k]=min(f[k],f[i]+work(i,k));
		}
	}
	printf("%d\n",ans);
	return 0;
}
</span>

by lych

2016.2.1 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值