집합상의 동적 기획
22529 단어 동적 기획
공간에 n개의 점 P0, P1,......Pn-1, 당신의 임무는 그들을 n/2쌍 (n은 짝수) 으로 배합하여 각 점이 한 점 맞도록 하는 것입니다.모든 점 대 중 두 점의 거리의 합은 가능한 한 작아야 한다.n<=20,|xi|,|yi|,|zi|<=1000.
샘플 입력:
201 2 31 1 15 6 24 7 82 3 11 4 72 5 83 6 91 2 52 3 64 5 27 8 54 5 1-1 2 3-1 -9 -70 0 0100 0 09 5 17 5 35 5 5
분석:
각 점이 짝을 지어야 하기 때문에 문제를 다음과 같은 다단계 결정 과정으로 보기 쉽다. 먼저 P0과 누가 짝을 지어야 하는지를 확정한 다음에 P1, 다음은 P2,......, 마지막은 Pn-1.앞의 사고방식에 따라 d(i)를 설정하면 앞의 i점 두 개를 짝짓는 최소 거리와 그 다음에 i점의 결정을 고려한다. 이것은 누구와 짝짓기를 합니까?가령 그것이 점 j와 짝짓기(j상태를 옮길 수 없다는 것을 발견한 후 흔히 볼 수 있는 방법은 차원, 즉 새로운 요소를 증가하고 상태를 더욱 세밀하게 묘사하는 것이다.방금'어떤 원소를 제외하고'는 언급했으니 상태의 일부분으로 삼아도 무방하다. d(i, S)를 설정하면 앞의 i점에서 집합 S에 있는 원소 두 쌍을 짝짓기하는 최소 거리를 나타낸다. 그러면 상태 이동 방정식은 다음과 같다.
d (i, S) = min {|Pipj|+ d (i-1, S-{i}-{j}) | j는 S}에 속함
여기서 |PiPj는 Pi와 Pj 사이의 거리를 나타냅니다.경계는 d (-1, S) = 0.
기억 검색의 코드는 다음과 같습니다.
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cmath>
4 #define maxn 20
5 #define INF (1<<30)
6 int n;
7 struct Point{
8 double x, y, z;
9 } p[maxn];
10 double f[(1<<maxn)];
11 inline double dist(int i, int j)
12 {
13 return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y) + (p[i].z-p[j].z)*(p[i].z-p[j].z));
14 }
15 inline double min(double x, double y)
16 {
17 if(x < y) return x;
18 return y;
19 }
20 double d(int S){
21 // !!( !! )
22 if(f[S] != -1) return f[S];
23 int i, j;
24 double& ans = f[S];
25 ans = (S == 0? 0 : INF);
26 for(i = n-1; i >= 0; --i) if(S & (1<<i)) break;
27 for(j = 0; j < i; ++j) if(S & (1<<j))
28 ans = min(ans, d(S^(1<<i)^(1<<j)) + dist(i, j));
29 return ans;
30 }
31
32 int main(){
33 int i;
34 scanf("%d", &n);
35 if(n%2) {printf("No answer!
"); return 0;}
36 for(i = 0; i < n; ++i) scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z);
37 for(i = 0; i < (1 << n); ++i) f[i] = -1;
38 printf("%.3lf
", d((1<<n)-1));
39 /*
40 int cnt = 0;
41 for(i = 0; i < (1 << n); ++i) if(f[i]!=-1) ++cnt;
42 printf("%d
", cnt);
43 */
44 return 0;
45 }
동적 계획 코드는 다음과 같습니다.
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cmath>
4 #define maxn 20
5 #define INF (1<<30)
6 int n;
7 struct Point{
8 double x, y, z;
9 } p[maxn];
10 double f[maxn][(1<<maxn)];
11 inline double dist(int i, int j)
12 {
13 return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y) + (p[i].z-p[j].z)*(p[i].z-p[j].z));
14 }
15 inline double min(double x, double y)
16 {
17 if(x < y) return x;
18 return y;
19 }
20 int main(){
21 int i,j;
22 scanf("%d", &n);
23 if(n%2) {printf("No answer!
"); return 0;}
24 for(i = 0; i < n; ++i) scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z);
25 for(i = 0; i < n; ++i)
26 for(int S = 0; S < (1 << (i+1)); ++S){
27 double& ans = f[i][S];
28 ans = (S==0 ? 0 : INF);
29 if(S & (1 << i)) {
30 for(j = 0; j < i; ++j) if(S & (1 << j))
31 ans = min(ans, f[i-1][S^(1<<i)^(1<<j)] + dist(i, j));
32 }
33 else if(i!=0) f[i][S] = f[i-1][S];
34 }
35 printf("%.3lf
", f[n-1][(1<<n)-1]);
36 return 0;
37 }
은연중 단계
위의 방정식은 한층 더 간소화할 수 있다.사실 단계 i는 저장할 필요가 없다. 이것은 이미 S에 숨겨져 있다. S에서 가장 큰 요소는 i이다.이렇게 하면 "S의 원소를 두 쌍으로 짝짓는 최소 거리와"를 직접 d(S)로 표시하면 상태 전이 방정식은 다음과 같다.
d(S)=min{|Pipj|+d(S-{i}-{j})|j는 S에 속하고 i=max(S)}
상태는 2n개이고 각 상태는 O(n)종의 전이 방식이 있으며 총 시간 복잡도는 O(n2n)이다.
특히 많은 사람들이 이런 상태로 방정식을 옮기고 있다.
d(S) =min{|Pipj|+d(S-{i}-{j}) | i, j는 S}
그것은 아까의 방정식과 매우 유사한데, 유일하게 다른 것은 i와 j는 모두 매거해야 한다는 것이다.이렇게 하는 것도 맞지만 각 상태의 이동 횟수는 O(n2)에 달하고 전체 시간의 복잡도는 O(n22n)로 아까의 방법보다 느리다.이 예는 같은 상태 묘사를 사용하면 결정을 줄이는 것도 중요하다는 것을 설명한다.
그러면 어떻게 S 중의 가장 큰 원소를 구할 수 있습니까?하나의 순환으로 판단하면 된다.S가 {0,1,2,..., n-1}의 모든 서브집합을 다 찾았을 때 평균 판단 횟수는 2에 불과합니다.
1 for(int S=0; S<(1<<n); S++)
2 {
3 int i,j;
4 d[S] = INF;
5 for(i=0; i<n; i++)
6 if(S && (1<<i)) break;
7 for(j=i+1; j<n; j++)
8 if(S & (1<<j)) d[S] <?= d[S^(1<<i)^(1<<j)] + dist(i, j));
9 }
상기 프로그램에서 구한 i는 S의 최소 요소이지 최대 요소가 아니지만 답안에 영향을 주지 않는다.또한 j의 매거는 i+1부터 시작한다. i가 S에서 가장 작은 원소인 이상 다른 원소는 자연히 i보다 크다는 것을 의미한다.마지막으로 설명해야 할 것은 S의 매거 순서다.발견하기 어렵지 않다. 만약에 S'가 S의 진짜 서브집합이라면 반드시 S'
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cmath>
4 #define maxn 22
5 #define maxs (1<<maxn)
6 #define INF 1e10
7 int n;
8 double f[maxs], x[maxn], y[maxn], z[maxn];
9 inline double min(double x, double y)
10 {
11 return x<y?x:y;
12 }
13 inline double dist(int i, int j)
14 {
15 return sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]) + (z[i]-z[j])*(z[i]-z[j]));
16 }
17 int main(){
18 scanf("%d", &n);
19 for(int i = 0; i < n; ++i) scanf("%lf%lf%lf", x+i, y+i, z+i);
20 int U = (1 << n);
21 for(int S = 0; S < U; ++S){
22 int i;
23 for(i = n-1; i >= 0; --i) if(S & (1<<i)) break;
24 f[S] = (S==0) ? 0 : INF;
25 for(int j = 0; j < i ; ++j) if(S & (1<<j)){
26 f[S] = min(f[S], f[S^(1<<i)^(1<<j)] + dist(i, j));
27 }
28 }
29 printf("%.3lf
", f[U-1]);
30 return 0;
31 }
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
01 가방, 완전 가방, 다중 가방 dp(동적 기획 입문 dp)01 가방은 2진법으로 직접 표시할 수 있지만 데이터 양이 너무 많으면 시간을 초과하는 것이 폭력이다.01 가방의 사상은 바로 이 물품에 대해 내가 넣은 가치가 큰지 안 넣은 가치가 큰지 비교하여 방정식 f[i][v...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.