1024 プログラマーの日 | C++ NOIP2014 普及グループ T3 スパイラル マトリックス

みなさん、こんにちは。今日は深刻な問題の解決策を投稿します。


ポータル:

1967年

ルオグ p2239


この問題は普及班の3問目ですが、実はそれほど難しくありません。でも、まあ、暴力が好きな人向けではありません

データ スケールを見てみましょう。

50\% データについて は、 1 \leqslant n \leqslant 100;

100\% データについて は、 1 \leqslant n \leqslant 30,000,1 \leqslant i \leqslant n,1 \leqslant j \leqslant n.

暴力的な場合、1 つは9 億の配列が追いつかないことであり、もう1 つはO\左 ( 900000000 \右 )時間の複雑さが許容できないことです。

ただし、これは浅くて弱い50%コードです。

#include <iostream>
using namespace std;
int a[105][105];
int main() {
    int n, x, y, m = 1; cin >> n >> x >> y;
    for (int i = 0; i < n / 2 + 1; ++i) {
        for (int j = i; j < n - i; ++j)a[i][j] = m++;
        for (int j = i + 1; j < n - i; ++j)a[j][n - i - 1] = m++;
        for (int j = n - i - 2; j > i; --j)a[n - i - 1][j] = m++;
        for (int j = n - i - 1; j > i; --j)a[j][i] = m++;
    }
    cout << a[x - 1][y - 1] << endl;
}

だから、別の方法を求めてください。


f(整数  n,int  i,int  j)この質問は、ルールが次のようになるたびに、関数を定義する再帰と見なすことができます。

最外層の場合、辺の長さを n として、次のように計算してみてください。

上に:戻る  j;

左に:戻る n+i-1;

右側に:戻る 3*n-2-d+1;

下:戻る 4*n-4-i+2;

そうでない場合は、1 つのレイヤーを内部にプッシュします。

戻るf(n-2,i-1,j-1)+4*(n-1);

つまり、このアルゴリズムは、ほとんどの場合、この行列のレベルまでプッシュする必要があるため、時間計算量はO(n/2)です。

コード:

#include<iostream>
using namespace std;
int f(int n, int i, int j) {
    if (i == 1)return j;
    if (j == n)return n + i - 1;
    if (i == n)return 3 * n - 2 - j + 1;
    if (j == 1)return 4 * n - 4 - i + 2;
    return f(n - 2, i - 1, j - 1) + 4 * (n - 1);
}
int main() {
    int n, i, j; cin >> n >> i >> j;
    cout << f(n, i, j) << endl;
}

私:しかし、私はまだO(n/2)不満があり、それを に変えたいと思っていO\left ( 1 \right )ます. 

聴衆: お元気ですか、コードについて考えたいと思います! これO\left ( 1 \right )はまったくできません!

私:しかし、この番号がどのリングにあるのか、このリングの最初の番号または前のリングの最後の番号を直接特定する方法はありますか?

観客:。. .

...

長い導出の後、私はそれを理解しました!


上記は段落であり、分析が正式に開始されます。

どのリングに属しているかを見つけるのは比較的簡単です。エッジからそれぞれ i と j の距離を確認するだけです。レイヤーの数を とするとへ、式は次のように簡略化されます。

f=min(min(i,j),(n+1)-max(i,j))

しかし、実際に判断文を使うのは、上下左右の4方向のうちどれが正しいかを知るためです。

このリングの最初の数を見つけるには、関係式を導出する必要があります。

(ここで、私は友人に一言言います: はい、それは私たちの先生が教えた「代数テスト」です)

前のリングの最後の数は、このレイヤーの外側の各レイヤーの周長の合計であるため、次のように設定しメートルます。

m=4(n-1)+4(n-3)+4(n-5)+...+4[n-(2f-3)]

     =4(n-1+n-3+n-5+...+n-2f+3)

     =4\左 \{ (f-1)n-[1+(2f-3)]*(f-1)/2\右 \}

     =4[(f-1)n-(2f-2)*(f-1)/2]

     =4[(f-1)n-(f-1)(f-1)]

     =4(f-1)(n-f+1)

このように、上記のポイントに従って4つのケースを計算できます。

さて、ここにアイデアがあります。書きましょう!

コード:

#include<iostream>
using namespace std;
int main() {
	int n, i, j, f, flag, m, ans, c; cin >> n >> i >> j;
	if (min(i, j) < (n + 1) - max(i, j)) {
		if (i < j)flag = 1;
		else flag = 4;
	}
	else {
		if (i > j)flag = 3;
		else flag = 2;
	}
	f = min(min(i, j), (n + 1) - max(i, j));
	m = 4 * (f - 1) * (n - f + 1);
	c = (n - 1) - 2 * (f - 1) + 1;
	i -= f - 1, j -= f - 1;
	if(flag == 1)ans = m + j;
	else if(flag == 2)ans = m + c + i - 1;
	else if(flag == 3)ans = m + 3 * c - j - 1;
	else ans = m + 4 * c - i - 2;
	cout << ans << endl;
	return 0;
}

さて、今日は深刻な問題の解決策を投稿しました。とても良いと思います。さようなら!

おすすめ

転載: blog.csdn.net/qq_43546083/article/details/127455268