题意:给定一个包含
n
n
n(
n
<
=
100
n<=100
n<=100)个点的无向连通图和一个长度为
L
L
L的序列
A
A
A(
L
<
=
200
L<=200
L<=200),你的任务是修改尽量少的数,使得序列中的任意两个相邻的数要么是相等的,要么是对应在图里面相邻的。
思路:要使得这个修改的数尽量少,不难发现找不到一种贪心的方法。使得数最少,考虑用
d
p
dp
dp解决,我们用
d
p
i
dp_i
dpi来维护使得序列
A
A
A变得合法所需要最少的花费。
然而,
d
p
i
−
1
dp_{i-1}
dpi−1对应的数字是何种会影响
d
p
i
dp_i
dpi的答案,所以我们要维护多一个维度来去使得答案是最小的.
令
d
p
i
,
j
dp_{i,j}
dpi,j表示把第
i
i
i个数字改为
j
j
j而且前
i
i
i个序列合法的费用.
不难有以下式子:
d
p
i
,
j
=
m
i
n
(
d
p
i
−
1
,
k
+
o
k
)
,
其
中
要
求
k
与
j
在
原
图
中
相
邻
,
当
i
≠
j
时
,
o
k
=
1
,
i
=
j
时
,
o
k
=
0
dp_{i,j}=min(dp_{i-1,k}+ok),其中要求k与j在原图中相邻,当i\neq j时,ok=1,i=j时,ok=0
dpi,j=min(dpi−1,k+ok),其中要求k与j在原图中相邻,当i=j时,ok=1,i=j时,ok=0
复杂度是
O
(
L
∗
n
2
)
O(L*n^2)
O(L∗n2),足以通过本题数据:
/*
Uvalive 4256
dp(i,j) 表示为把第i位改成数字j,而且前i位均合法的答案.
j!=i,dp(i,j)=min{dp(i-1,k) | {k与j在原图中相邻} } + 1.
i==j,dp(i,i) = min{ dp{i-1,k) | {k与i在原图中相邻} } .
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100+7;
const int INF = 1e9+7;
int G[maxn][maxn];
int a[2*maxn];
int dp[2*maxn][maxn];
void _init(int n){
for(int i=0;i<=n;i++) {
for(int j=0;j<=105;j++) dp[i][j] = INF;
}
}
int main(){
int T;cin>>T;
while(T--){
int n,m;
scanf("%d %d",&n,&m);
memset(G,0,sizeof(G));
for(int i=1;i<=m;i++){
int u,v;scanf("%d %d",&u,&v);
G[u][v]=G[v][u]=1;
}
int L;scanf("%d",&L);
_init(L);
for(int i=1;i<=L;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) {
dp[1][i] = (i==a[1]) ? 0 : 1;
G[i][i]=1;
}
for(int i=2;i<=L;i++){
for(int j=1;j<=n;j++){
int ok=(!(a[i]==j));//如果a[i]==j ok=0;否则ok=1;
for(int k=1;k<=n;k++){
if(G[k][j]) dp[i][j] =min(dp[i][j],dp[i-1][k]+ok);
}
}
}
int ans = INF;
for(int i=1;i<=n;i++){
ans=min(ans,dp[L][i]);
}
cout<<ans<<"\n";
}
return 0;}