kmjp's blog

競技プログラミング参加記です

yukicoder : No.385 カップ麺生活

遅れて参加したけど全完出来て良かった。
http://yukicoder.me/problems/no/385

問題

M円の所持金を持った状態で、N種類のカップ麺を買う。
各カップ麺の価格はC[i]で、それぞれ何個でも購入可能である。

カップ麺をいくつか買って所持金が素数になった場合、所持金をM円に戻せる。
ただし各素数からは1回ずつしかM円に戻せない。

最適な順でカップ麺購入・所持金戻しをするとき、最大何個カップめんを変えるか。

解法

まず個数制限なしナップサック問題を解き、i円で変えるカップ麺の最大数f(i)を求めよう。

そして各素数1<p≦Mに対し、残金をp円にする(そしてM円に戻す)ような買い方として、f(M-p)個ずつ選ぶ選び方がある。
よってそのようなf(M-p)の総和を求めよう。
最後に、所持金を戻すことなく持てるお金で買えるだけカップ麺を買いたいので、f(i)の最大値を加えるとそれが解。

int M,N;
int C[21];

const int prime_max = 1000000;
int NP,prime[100000],divp[prime_max];

int dp[101010];


void cprime() {
	for(int i=2;i<prime_max;i++) if(divp[i]==0) {
		//M[i]=NP;
		prime[NP++]=i;
		for(ll j=1LL*i*i;j>i&&j<prime_max;j+=i) divp[j]=i;
	}
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cprime();
	
	cin>>M>>N;
	FOR(i,N) cin>>C[i];
	sort(C,C+N);
	
	memset(dp,0xec,sizeof(dp));
	dp[0]=0;
	FOR(i,N) for(x=0;x+C[i]<=M;x++) dp[x+C[i]] = max(dp[x+C[i]],dp[x]+1);
	
	int ma=0;
	FOR(i,M+1) ma=max(ma,dp[i]);
	for(i=1;i<=M-2;i++) if(divp[M-i]==0 && dp[i]>0) ma += dp[i];
	cout<<ma<<endl;
	
}

まとめ

カップ麺79842個買うなら、別のもの買った方がいいんじゃないですかね…。