タイトル説明
この質問にはleetcodeのメンバーシップが必要なので、lintcodeにアクセスしました。
https://www.lintcode.com/problem/867/
解決
初めての解決策初めて簡単にできると思ったので、次のコードを書きました。
public class Solution {
/**
* @param N: an integer
* @return: return an integer
*/
public int maxA(int N) {
// write your code here
if(N<0) return -1;
if(N<=3) return N;
int []dp = new int[N+1];
dp[0]=0;
dp[1]=1;
dp[2]=2;
dp[3]=3;
int cache=0;//记录缓冲区的数据量大小
for(int i=4;i<=N;i++){
//要保证最大,
if(dp[i-3]>=3 && i-3>=3 && dp[i-3]>3*cache){
//是否能按下c-A\c-c\c-v,同时判断按下3次c-V的收益大还是按下3次c-A\c-c\c-v的收益大
dp[i] = 2*dp[i-3];//全选、复制、黏贴
cache = dp[i-3];
// System.out.println(cache+" --");
}else if(cache!=0){
dp[i] = dp[i-1]+cache;//只剩下一次机会了
}else{
//只能一个一个按
dp[i] = dp[i-1]+1;
}
}
return dp[N];
}
}
実際、これは間違っています。前の状態の選択と後者の状態の選択は依存しておらず、最大の選択関係だからです。たとえば、A、A、A、CA、CC、CVを6回押すと、6つのAが得られますが、7回押すと、7回目のCVを直接押すと9つのAが得られます。
しかし、今100回押すことができれば、10回押すと10回押す結果とは異なる場合があります。
2回目は、ブルートフォースクラッキング-再帰
直接思考では不十分なので、最初に再帰的に考える必要があります。力ずくの方法です。
public class Solution {
/**
* @param N: an integer
* @return: return an integer
*/
public int maxA(int N) {
return findMaxA(N,0,0);
}
public int findMaxA(int times,int A_nums,int cacheNums){
if(times<=0) return A_nums;
return Math.max(
//按下A
findMaxA(times-1,A_nums+1,cacheNums),
Math.max(
//按下C-V
findMaxA(times-1,A_nums+cacheNums,cacheNums),
//按下C-A,C-C
findMaxA(times-2,A_nums,A_nums)//缓冲区数目直接变为A_nums
)
);
}
}
サブ問題が重複しすぎる可能性があるため、結果はタイムアウトになります。この問題のため:
ここで100回押すことができれば、10回押すと10回押す結果とは異なる場合があります。
したがって、メモを使用するときは、再利用を避けるためにトリプルを使用してください。だからここには書きません。
3番目の方法-dp配列
を再定義することがわかります:
A:A、A、... Aを押し続ける(Nが比較的小さい場合)。
A、A、…CA、CC、CV、CV、…CV(Nが比較的大きい場合)のいずれかの形式です。
次に、動的再帰のプロセスについて考えます。
- 変数とは何ですか?キーストロークの数、Aの数
- dp [i]は、キーをi回押して、ディスプレイAの最大結果を示します。
オプションについて考える:
-
最後のキーストロークは、Aを押すか、CVを押して直接コピーするか、CA、CC、CVを押すことでした。
Aを押す場合、最後の状態にAが追加されたことがわかります。つまり、dp [i] = dp [i-1] +1です。 -
CVを押すのはどうですか?CAとCCがその前に表示される必要があることはわかっています。データをバッファーにコピーするには、CVを使用できます。しかし、その場所は不確かです。次に、CVの最も早い開始点を記録する必要があります。次に、この開始点の前の2つの操作はCA、CCである必要があります。この出発点は私の前にしかあり得ないので、私たちは直接暴力を振るう!
次に、2つの選択肢の中で、現在のdp [i]が最大の結果を取得することによって取得されます。
int[]dp = new int[N+1];
dp[0] = 0;
for(int i=1;i<=N;i++){
//选择按下A,
dp[i] = dp[i-1]+1;
//选择最后一次操作为按下C-V
for(int j=2;j<i;j++){
//j必然大于2才可能按下c-v,记录C-V的最早的起点
//这里全选+复制,那么我们得到了 dp(j-2)缓冲区数目,然后连续按下复制键j-i+1次
dp[i] = Math.max(dp[i],dp[j-2]*(i-j+1));
//这里就是暴力遍历,假设每个j都是C-V的最早的起点,找到最大值
}
}