P2747 [USACO5.4]周游加拿大Canada Tour DP
1030考试T4
题目大意:
小 R 赢得了一张 L 国家环游机票。旅行在这家航空公司开放的最西边的城市开始,然后一直自西向东旅行,直到你到达最东边的城市,再由东向西返回,直到你回到开始的城市。除了旅行开始的城市之外,每个城市只能访问一次,因为开始的城市必定要被访问两次(在旅行的开始和结束)。当然不允许使用其他公司的航线或者用其他的交通工具。给出这个航空公司开放的城市的列表,和两两城市之间的直达航线列表。找出能够访问尽可能多的城市的路线,这条路线必须满足上述条件,也就是从列表中的第一个城市开始旅行,访问到列表中最后一个城市之后再返回第一个城市。
题目链接
DP.
题目让你找一条从\(n\)到1.再从1到\(n\)并且不重复经过点的最长路径.从\(n\)到1的路径可以看做是从1到\(n\)的另一条路径,所以我们可以设:
\(f[i][j]\)表示第一条路径走到\(i\),第二条路径走到\(j\)的最长路径,所以最后的答案应该是\(max(f[i][n])\).保证\(i,n\)联通.
然后转移:\(f[i][j] = f[i][k] + 1\),保证\(k, j\)联通,也就是\(f[i][k]\)有值.因为着两条路径可以交换,所以\(f[j][i] = f[i][j]\).强制\(j > i, j > k\)是为了防止重复经过一个点.
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 3005;
int n, m, ans;
string a, b;
int vis[N][N], f[N][N];
map <string, int> mp;
int main() {
cin >> n >> m;
for(int i = 1;i <= n; i++) {
cin >> a; mp[a] = i;
}
for(int i = 1;i <= m; i++) {
cin >> a >> b;
vis[mp[a]][mp[b]] = vis[mp[b]][mp[a]] = 1;
}
memset(f, -63, sizeof(f)); f[1][1] = 1;
for(int i = 1;i <= n; i++)
for(int j = i + 1;j <= n; j++)
for(int k = 1;k < j; k++)
if(vis[j][k])
f[i][j] = f[j][i] = max(f[i][j], f[i][k] + 1);
ans = 1;
for(int i = 1;i <= n; i++)
if(vis[i][n]) ans = max(ans, f[i][n]);
printf("%d", ans);
return 0;
}