タイトル
問題の説明
:とする場合にのみあれば我々は、興味深いの数と呼ば
1.それは数字0、1、2、3のみ含まれており、これらの4つの数字が一度少なくとも登場しています。
2.すべての0は1、2の全ての前に表示され、すべての3つのすべての前に表示されます。
3.最上位桁はゼロではありません。
したがって、興味深いの最小数は、2013年の私たちの定義に沿ったものです。2031年および2301:また、興味深い数4は、2つあります。
正確にnビットの興味深い数の数を計算してください。答えは非常に大きくなる可能性があるため、唯一の答えは十億七の出力で割って必要があります。
入力フォーマット
だけ正の整数N(4≤N≤1000)を含む入力一行 。
出力フォーマットの
出力数は、ちょうどn十億七の残りの部分で割っ面白いビット整数を含む唯一のラインです。
入力サンプル
4
サンプル出力
3
私のコード
#include <iostream>
#include <cstring>
using namespace std;
long long dps[4];//因为题目中明确可能性会超过十亿零七,所以用long long数据类型存储,该数据类型取值范围为-2^63~2^63-1,可以承受
long long dpsq[1001][2][2][2][2][2][4];
long long dp(int n,bool flag,int branch) {
int flag0=(dps[0]>0),flag1=(dps[1]>0),flag2=(dps[2]>0),flag3=(dps[3]>0);
long long ans=0;
if(dpsq[n][flag0][flag1][flag2][flag3][flag][branch]!=-1)
return dpsq[n][flag0][flag1][flag2][flag3][flag][branch];
if(n<4&&flag)
return 0;
if(n<=1)
{
if(dps[0]>0&&dps[1]>0&&dps[3]>0&&n==1)
return 1;
else
return 0;
}
else
{
if(flag)
{
ans+=2*dp(n-1,true,0);
dps[1]++;
ans+=dp(n-1,false,1);
dps[1]--;
dps[3]++;
ans+=dp(n-1,false,3);
dps[3]--;
}
else
{
if(branch==1)
{
if(dps[3]>0)
{
dps[2]++;
ans+=dp(n-1,false,branch);
dps[2]--;
}
if(dps[2]==0)
{
dps[3]++;
ans+=dp(n-1,false,branch);
dps[3]--;
}
dps[0]++;
ans+=dp(n-1,false,branch);
dps[0]--;
}
else
{
if(dps[1]>0)
{
dps[0]++;
ans+=dp(n-1,false,branch);
dps[0]--;
}
if(dps[0]==0)
{
dps[1]++;
ans+=dp(n-1,false,branch);
dps[1]--;
}
dps[2]++;
ans+=dp(n-1,false,branch);
dps[2]--;
}
}
}
ans %= 1000000007;
dpsq[n][flag0][flag1][flag2][flag3][flag][branch]=ans;
return ans;
}
int main() {
int n=false;
cin >> n;
dps[0]=dps[1]=dps[2]=dps[3]=0;
memset(dpsq,-1,sizeof(dpsq));
long long ans = dp(n,true,0);
cout << ans << endl;
return 0;
}
私の考え
基本的な考え方は、最終結果は確かに多くの10億以上であることを考慮すると、動的計画で、既存のカウンタプラスの方法は、少なくとも10の万回を追加する場合ので、あなたは間違いなくアウト動的計画を持っていない場合。
そして、興味深いビット数nに対して、再帰の言葉を考えます
- あなたは面白いのn-1、またはビット3組成物1の数を追加することができます
- これは、n-1ビット1又は3の欠如が、配列番号プラスつのルールが満たされる欠く又は3組成物であってもよいです
この場合、動的なプログラミングのアイデアの組み合わせは、私が保存された再帰関数の結果を検討する必要があり、その後、再帰的プロセスを検討
少し打ち上げに簡単に、ここでのn-1ビットの数の再帰非再帰的なアイデアを複雑明確な得ることができます再帰的ツリーを塗装して、詳細には触れません
その後、再帰木と組み合わせて、全体のコード再帰的プロセスを取得し
、再帰的な輸出:
- 以前は関数値を計算し
- 興味深いの数であるNながら4時間の割合を算出する際に、楽しい以下4数は、戻り存在しない0
- 場合N末端に1つの再帰2桁に加えて、検出が完了したならば、次に、それだけ2,0の分析を介して以来最高レベル、1を返し、さもなければ、少なくとも4つの数値を満たしていない最初の後ろに加えて現れますルール
再帰的な手順
- 興味深いの数を計算するための最初の方法
- 非面白い徒歩ブランチ1の数を計算する2番目の方法は存在しません。
- 非面白い徒歩3分枝の数を計算するための第三の方法は存在しません。
ACアウト私のアイデアのソリューションがより速くなるように見えるこの質問は、ディスプレイは0msとあるが、最終的に混乱少しは本当に速いです、または他の理由のために、人々はコメント欄が私に教えて知られたくないことを、あなたに感謝します
回答やコメント解析されました
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <deque>
#include <list>
using namespace std;
long long f[2000][3][2];//因为题目中明确可能性会超过十亿零七,所以用long long数据类型存储,该数据类型取值范围为-2^63~2^63-1,可以承受
int dp(int n, int p1, int p3) {
long long &now = f[n][p1][p3];
if (now != -1) return now;
if (n == 0) {
if (p1 == 2 && p3 == 1) {
now = 1;
} else {
now = 0;
}
return now;
}
now = 0;
if (p1 == 0) {
now += dp(n-1, 1, p3);
} else if (p1 == 1) {
now += dp(n-1, 1, p3);
now += dp(n-1, 2, p3);
} else {
now += dp(n-1, 2, p3);
}
if (p3 == 0) {
now += dp(n-1, p1, p3);
now += dp(n-1, p1, 1);
} else {
now += dp(n-1, p1, 1);
}
now %= 1000000007;
}
int main() {
int n;
cin >> n;
memset(f, -1, sizeof(f));
//初始化三维数组f,每个元素都为-1
int ans = dp(n - 1, 0, 0);
cout << ans << endl;
return 0;
}