题意:给定一个包含n(n <= 100)个点的无向联通图和一个长度为L的序列A(L <= 200),你的任务是修改尽量少的数,使得序列中的任意两个相邻数或者相同,或者对应图中的两个相邻节点。
思路:用dp[i][j]表示从1到位置i以j为结尾的最小修改的数,则答案为max(dp[L][1~n]).
如果a[i] == j, dp[i][j] = min(dp[i - 1][j], dp[i - 1][k]) 。否则dp[i][j] = min(dp[i - 1][j] + 1, dp[i - 1][k] + 1) j与k为相邻节点。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include<vector>
#include<cmath>
#include<set>
#include<cstring>
#include<map>
using namespace std;
const int maxn = 21e2 + 10;
const int maxt = 100200;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
const double eps = 1e-8;
typedef long long ll;
vector<int> g[110];
int dp[210][110];
int a[220];
void init(){
for(int i = 1; i <= 100; ++i) g[i].clear();
memset(dp, INF, sizeof dp);
}
int main(){
int T;
scanf("%d", &T);
while(T--){
int n, m;
init();
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i){
int x, y;
scanf("%d%d", &x, &y);
g[x].push_back(y);
g[y].push_back(x);
}
int l;
scanf("%d", &l);
for(int i = 1; i <= l; ++i)
scanf("%d", &a[i]);
dp[1][a[1]] = 0;
for(int i = 2; i <= l; ++i){
for(int j = 1; j <= n; ++j){
if(j == a[i])
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = dp[i - 1][j] + 1;
for(int k = 0; k < g[j].size(); ++k){
int u = g[j][k];
if(j == a[i])
dp[i][j] = min(dp[i][j], dp[i - 1][u]);
else
dp[i][j] = min(dp[i][j], dp[i - 1][u] + 1);
}
}
}
int ans = INF;
for(int i = 1; i <= n; ++i) ans = min(ans, dp[l][i]);
printf("%d\n", ans);
}
}