PKU1032
PKU1032を解いている途中。
問題、N(5<=N<=1000)が与えられたとき、Nを異なるいくつかのグループに分割する(例えば、N=5の時、5=2+3のように)。このとき、分割したグループの積が最大となる分割の仕方の出力せよ。
解答を考える。DPっぽい感じがする。例えば、こんな感じ。
F[i,j] => i以上のグループでのjの分割した時のグループの積の最大値
F[i,0] = 1 (i>=0の時、分割していくとここに行きつく)
F[i,j] = 0 (i>jの時,このとき分割はできない)
F[i,j] = iからjの数で分割する。kをiからjの数とすると、部分問題は、F[k+1,j-k]となる。このkをiからjで動かした時のmax( i*F[k+1,j-k] )がこの値になる。
で、以下がこれをメモ化再帰でコード化したもの
#include<iostream> #include<vector> using namespace std; double memo[1001][1001]={{-1}}; pair<int,int> record[1001][1001]; int memotan(int p,int rest){ if(rest==0 ) return 1; if(p>rest) return 0; if(memo[p][rest]!=-1) return memo[p][rest]; double mx = -1; int ii; for(int i=p;i<=rest;i++){ double temp = memotan(i+1,rest-i); if(mx < i*temp){ mx = i*temp; ii=i; } } memo[p][rest]=ii; memo[p][rest]=mx; record[p][rest]=pair<int,int>(ii,rest); return memo[p][rest]; } void output(int p,int rest){ if(rest==0) return ; output(record[p][rest].first+1, record[p][rest].second-record[p][rest].first); cout << record[p][rest].first << " "; } int main(){ for(int i=0;i<1001;i++) for(int j=0;j<1001;j++) memo[i][j]=-1; memo[0][0]=1; int N;cin >>N; for(int i=0;i<N;i++){ int c; cin >> c; cout << c << endl; memotan(2,c); output(2,c); cout << endl; } return 0; }
と、このコードでは正しくない。桁あふれが発生しているから値が大きくなると死ぬ。
んー、どうするかと今考えているところ。小さい値で試すと、いくつか法則性が見つかるこれをコード化してみようと思う。
いくつか、小さい順に解を示していくと、
5 -> 2 3 - 6 -> 2 4 | 2つのグループは,4つ 7 -> 3 4 | 8 -> 3 5 - 9 -> 2 3 4 - 10 -> 2 3 5 | 11 -> 2 4 5 | 3つのグループは,5 12 -> 3 4 5 | 13 -> 3 4 6 - 14 -> 2 3 4 5 - 15 -> 2 3 4 6 | 16 -> 2 3 5 6 | 4つのグループは,6 17 -> 2 4 5 6 | 18 -> 3 4 5 6 | 19 -> 3 4 5 7 -
となる。さらに、各グループでは、1つ前のグループに、後ろから1づつ足していったもの。
例 14 -> 2 3 4 5 +1 15 -> 2 3 4 6 +1 16 -> 2 3 5 6
これを、単純に実装すると、
#include<iostream> #include<vector> using namespace std; int group[1001]={0}; int main(){ int k=5; //各数字がいくつの要素のグループかを先に書いておく for(int i=4;i<1001;i++){ for(int j=0;j<i;j++){ if(k+j>1000) break; group[k+j] = i-2; } k+=i; } int n; cin >> n; vector<int> ans; int sm = 0; //2から順に連続したgroup[n]個を答えに入れる。 for(int i=0;i<group[n];i++){ ans.push_back(i+2); sm += i+2; } int j=ans.size()-1; //答えの合計がn二届かない分後ろから1足していく while(sm<n){ ans[j]++; sm+=1; j--; if(j==-1) j=ans.size()-1; } for(int i=0;i<ans.size();i++){ cout << ans[i]; if(i!=ans.size()-1) cout << " "; } return 0; }
とこれで、やっとPKUでacceptされた。ふぅ