1024 프로그래머의 날 C++ NOIP2014 대중화단체 T3 나선행렬

안녕하세요 여러분, 오늘은 심각한 문제 해결 방법을 포스팅하려고 합니다.


문:

1967년

뤄구 p2239


이 질문은 대중화 그룹의 세 번째 질문이지만 실제로는 그리 어렵지 않습니다. 하지만 폭력을 좋아하는 사람들에게는 그렇지 않습니다 .

데이터 규모를 살펴보십시오.

50\% 데이터의 경우  1 \leqslant n \leqslant 100;

100\% 데이터의 경우  1 \leqslant n \leqslant 30,000,1 \leqslant i \leqslant n,1 \leqslant j \leqslant n.

폭력적이라면 하나는 9억 어레이 가 따라가지 못한다는 것이고, 다른 하나는 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(int  엔, 정수  나, 정수  제이)이 질문은 규칙이 다음과 같을 때마다 함수를 정의하는 재귀로 간주될 수 있습니다 .

가장 바깥쪽 레이어에 있는 경우 n을 측면 길이라고 하고 다음을 계산해 봅니다.

상단:반품  제이;

왼쪽에:반품 n+i-1;

오른쪽으로:반품 3*n-2-d+1;

아래에:반품 4*n-4-i+2;

그렇지 않은 경우 내부에 하나의 레이어를 밀어 넣습니다.

반품f(n-2,i-1,j-1)+4*(n-1);

즉, 이 알고리즘 은 대부분의 경우 이 행렬의 수준 으로 푸시되어야 하므로 시간 복잡도는 오(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;
}

나: 하지만 여전히 오(n/2)불만족스러워서 로 바꾸고 싶어요 O\왼쪽 ( 1 \오른쪽 )

청중: 어때요, 코드에 대해 생각하고 싶어요! 전혀 할 수 없습니다 O\왼쪽 ( 1 \오른쪽 )!

나: 하지만, 이 번호가 어느 링에 있는지, 이 링의 첫 번째 번호 또는 이전 링의 마지막 번호가 무엇인지 직접 확인할 수 있는 방법이 있습니까?

청중:. . .

...

오랜 고민 끝에 알아냈어요!


위의 내용은 한 문단이며 공식적으로 분석이 시작됩니다.

어느 링이 속하는지 찾는 것은 비교적 간단합니다. 가장자리에서 각각 i와 j의 거리를 살펴보십시오. 레이어 수 에프를 이라고 하면 표현식이 다음과 같이 단순화됩니다.

f=최소(최소(i,j),(n+1)-최대(i,j))

그러나 우리는 실제로 위, 아래, 왼쪽, 오른쪽 네 방향 중 어느 방향인지 알아내기 위해 판단문을 사용합니다.

이 고리의 첫 번째 숫자를 찾으려면 관계식을 도출해야 합니다.

(여기서, 나는 내 친구에게 한마디 : 네, 그것은 우리 선생님이 가르치는 "대수학 시험"입니다)

이전 링의 마지막 숫자는 이 레이어 외부의 각 레이어 둘레의 합이므로 다음과 같이 설정합니다 중.

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
рекомендация