小Y的地铁

Time Limit: 50 Sec Memory Limit: 256 MB

Description

img

Input

img

Output

对于每组输入数据,输出一行一个整数,表示除掉这 n 个换乘站之外,最少有几个换乘站。

Sample Input

4
  4
  1 2 1 2
  8
  1 2 3 4 1 2 3 4
  5
  5 4 3 3 5
  8
  1 2 3 4 1 3 2 4

Sample Output

0
  0
  0
  1

HINT

n <= 44

Solution

首先,答案显然只和几个区域的连通状态有关,那么我们可以写出四种本质不同的方案。(即下图中被线分开的六块)。

img

我们可以考虑,对于一条线,其他线(显然仅有 部分相交完全相交 两种)造成的贡献。打出表来,上图是不会造成交点的线段种类

既然知道了这个,我们的复杂度显然可以做到 O(4 ^ (n / 2))。还是不足以通过,怎么办呢?

模拟退火大法好!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include<bits/stdc++.h>
using namespace std;
typedef long long s64;

const int ONE = 105;
const int INF = 2147483640;

int get()
{
int res = 1, Q = 1; char c;
while( (c = getchar()) < 48 || c > 57)
if(c == '-') Q = -1;
if(Q) res = c - 48;
while( (c = getchar()) >= 48 && c <= 57)
res = res * 10 + c - 48;
return res * Q;
}

int n, num;
int pos[ONE], val[ONE];
int vis[ONE], a[ONE];
int Ans = INF;
struct power {int l, r;} A[ONE];

int x[ONE][ONE], y[ONE][ONE];

void Deal_first()
{
x[1][2] = x[1][4] = x[1][5] = 1;
x[2][1] = x[2][3] = x[2][6] = 1;
x[3][1] = x[3][3] = x[3][6] = 1;
x[4][2] = x[4][4] = x[4][5] = 1;
for(int i = 1; i <= 4; i++) y[i][1] = y[i][2] = 1;
}

int Now;

int Judge(int pos, int type)
{
int res = Now;
for(int i = pos, j = pos + 1; j <= num; j++)
{
if(A[i].r < A[j].l) continue;
if(A[i].r < A[j].r) res -= !x[a[i]][a[j]];
if(A[j].r < A[i].r) res -= !y[a[i]][a[j]];
}
for(int i = 1, j = pos; i < pos; i++)
{
if(A[i].r < A[j].l) continue;
if(A[i].r < A[j].r) res -= !x[a[i]][a[j]];
if(A[j].r < A[i].r) res -= !y[a[i]][a[j]];
}

a[pos] = type;

for(int i = pos, j = pos + 1; j <= num; j++)
{
if(A[i].r < A[j].l) continue;
if(A[i].r < A[j].r) res += !x[a[i]][a[j]];
if(A[j].r < A[i].r) res += !y[a[i]][a[j]];
}
for(int i = 1, j = pos; i < pos; i++)
{
if(A[i].r < A[j].l) continue;
if(A[i].r < A[j].r) res += !x[a[i]][a[j]];
if(A[j].r < A[i].r) res += !y[a[i]][a[j]];
}

Now = res, Ans = min(Ans, res);
return res;
}

double Random() {return (double)rand() / RAND_MAX;}
void SA()
{
if(num == 0) return;
double T = num * 2;
while(T >= 0.01)
{
int pos = rand() % num + 1, type = rand() % 4 + 1;
int ori = Now, ori_type = a[pos];

int dE = Judge(pos, type) - ori;
if(dE <= 0 || Random() <= exp(-dE / T)) a[pos] = type;
else Judge(pos, ori_type);

T *= 0.9993;
}
}

void Deal()
{
Ans = INF;
n = get();
for(int i = 1; i <= n; i++) a[i] = get(), pos[a[i]] = vis[a[i]] = 0;
for(int i = n; i >= 1; i--)
if(!pos[a[i]]) pos[a[i]] = i;

num = 0;
for(int i = 1; i <= n; i++)
if(!vis[a[i]] && pos[a[i]] != i)
A[++num] = (power){i, pos[a[i]]}, vis[a[i]] = 1;

for(int i = 1; i <= num; i++)
a[i] = rand() % 4 + 1;
Ans = 0;
for(int i = 1; i <= num; i++)
for(int j = i + 1; j <= num; j++)
{
if(A[i].r < A[j].l) break;
if(A[i].r < A[j].r) Ans += !x[a[i]][a[j]];
if(A[j].r < A[i].r) Ans += !y[a[i]][a[j]];
}
Now = Ans;
for(int i = 1; i <= 10; i++)
SA();
printf("%d\n", Ans);
}

int main()
{
Deal_first();
int T = get();
while(T--)
Deal();
}