前提・実現したいこと
問題文は以下の通り。
「高橋君は、N枚のカードを持っています。 i(1≤i≤N) 番目のカードには、整数 xi が書かれています。 高橋君は、これらのカードの中から 1 枚以上を選び、 選んだカードに書かれた整数の平均をちょうど A にしたいと考えています。 そのようなカードの選び方が何通りあるか求めてください。」
動的計画法を用いて漸化式を立てると次の通り。
if (j >= x[i]&&k > 0) {dp[i + 1][j][k] = dp[i][j - x[i]][k - 1] + dp[i][j][k];}
else {dp[i + 1][j][k] = dp[i][j][k];}
i:ここまでで選ぶかどうか判断し終えたカードの番号
j:選んだカードたちの値の合計
k:実際に選んだカードの枚数
これを全通りに対し実行し、最後にdp[N][kA][k]をすべてのkについて合計すれば答えが得られる。(Aは平均値であるから。j=kAとすれば良い。)
発生している問題・エラーメッセージ
質問は二点。
まず一点目。私は、
for (int i = 0; i < N; i++) {
for (int k = 0; k <= N; k++) {
for (int j = 0; j <= sum; j++) {}}}
と素直にすればいいものの、
for (int i = 0; i < N; i++) {
for (int k = 0; k <= i; k++) {
for (int j = 0; j <= sum; j++) {}}}
とした。なぜなら、ここまでで選ぶかどうか考慮し終えた番号iを、実際に選んだ枚数kが超えることなどありえないから、当然i<kのときの場合の数は0通りになるので考慮する必要がないと考えたからだ。すると、誤った答えが得られた。これはなぜだろうか。
質問二点目。
次に、質問一点目の内容を正しいソースコードに書き換えたが、最後にdp[N][kA][k]をすべてのkについて合計するところで、1≤k≤Nで合計するのではなく、1≤k≤n(但しint n=sum/a,sumは全xiの合計値)で合計した。なぜなら、選んだカードの合計値jが、nAを超えて、かつkが整数となることは数学的にあり得ないので、n<k≤Nなるkを考慮する必要はないと考えたからである。
すると、またしても誤った答えが得られた。なぜだろうか。
入門者なりに散々考えた末の質問です。どうか核心を突いた明快なご指摘を頂きたいです。よろしくお願い致します。
該当の(誤った)ソースコード
C++
1#include <iostream> 2using namespace std; 3 4int main() { 5 int N, A; 6 cin >> N >> A; 7 int x[53]; 8 int sum = 0; 9 for (int i = 0; i < N; i++) { 10 cin >> x[i]; 11 sum += x[i]; 12 } 13 long long dp[53][2550][53] = { 0 }; 14 dp[0][0][0] = 1; 15 for (int i = 0; i < N; i++) { 16 for (int k = 0; k <= i; k++) { 17 for (int j = 0; j <= sum; j++) { 18 if (j >= x[i]&&j>0) { 19 dp[i + 1][j][k] = dp[i][j - x[i]][k - 1] + dp[i][j][k]; 20 } 21 else { 22 dp[i + 1][j][k] = dp[i][j][k]; 23 } 24 } 25 } 26 } 27 long long combi; 28 combi = 0; 29 int n = 0; 30 n = sum / A; 31 for (int i = 1; i <= n; i++) { 32 combi += dp[N][i*A][i]; 33 } 34 cout << combi; 35 return 0; 36}
正しいソースコード
#include <iostream>
using namespace std;
int main() {
int N, A;
cin >> N >> A;
int x[53];
int sum = 0;
for (int i = 0; i < N; i++) {
cin >> x[i];
sum += x[i];
}
long long dp[53][2550][53] = { 0 };
dp[0][0][0] = 1;
for (int i = 0; i < N; i++) {
for (int k = 0; k <= N; k++) {
for (int j = 0; j <= sum; j++) {
if (j >= x[i]&&k > 0) {
dp[i + 1][j][k] = dp[i][j - x[i]][k - 1] + dp[i][j][k];
}
else {
dp[i + 1][j][k] = dp[i][j][k];
}
}
}
}
long long combi;
combi = 0;
int n = 0;
n = sum / A;
for (int i = 1; i <= N; i++) {
combi += dp[N][i*A][i];
}
cout << combi;
return 0;
}
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/09 02:52