Usaco 5.4.2 tour
【关键字】
双线程动态规划
【摘要】
顺次排开的点,要求再不重复点和边的情况下,从最左边的点走到最右边的点,在回到最左边的点,所能经过最多的城市。
【正文】
1.题目描述
USACO/tour
描述
你赢得了一场航空公司举办的比赛,奖品是一张加拿大环游机票。旅行在这家航空公司开放的最西边的城市开始,然后一直自西向东旅行,直到你到达最东边的城市,再由东向西返回,直到你回到开始的城市。除了旅行开始的城市之外,每个城市只能访问一次,因为开始的城市必定要被访问两次(在旅行的开始和结束)。
当然不允许使用其他公司的航线或者用其他的交通工具。
给出这个航空公司开放的城市的列表,和两两城市之间的直达航线列表。找出能够访问尽可能多的城市的路线,这条路线必须满足上述条件,也就是从列表中的第一个城市开始旅行,访问到列表中最后一个城市之后再返回第一个城市。
格式
PROGRAM NAME: tour
INPUT FORMAT
Line 1: 航空公司开放的城市数 N 和将要列出的直达航线的数量V。N 是一个不大于 100 的正整数。V 是任意的正整数。
Lines 2..N+1: 每行包括一个航空公司开放的城市名称。城市名称按照自西向东排列。不会出现两个城市在同一条经线上的情况。每个城市的名称都是一个字符串,最多15字节,由拉丁字母表上的字母组成;城市名称中没有空格。
Lines N+2..N+2+V-1: 每行包括两个城市名称(由上面列表中的城市名称组成),用一个空格分开。这样就表示两个城市之间的直达双程航线。
OUTPUT FORMAT
Line 1: 按照最佳路线访问的不同城市的数量 M。如果无法找到路线,输出1。
SAMPLE INPUT (file tour.in)
8 9
Vancouver
Yellowknife
Edmonton
Calgary
Winnipeg
Toronto
Montreal
Halifax
Vancouver Edmonton
Vancouver Calgary
Calgary Winnipeg
Winnipeg Toronto
Toronto Halifax
Montreal Halifax
Edmonton Montreal
Edmonton Yellowknife
Edmonton Calgary
SAMPLE OUTPUT (file tour.out)
7
2.算法分析
把返回的路线反向,那么整条路线就变成了两条不相交的从起点到终点的路线。这样我们可以用DP解决。
状态设定
f[i,j] 为假定的甲乙两人,甲走到第i个城市,乙走到第j个城市时,两人走过的城市数目的和。
初始状态
f[1,1]=1
状态转移方程
f[j,i]=f[i,j]=max{f[i,k]+1}(k到j存在飞机航线,以及f[i,k]>0,就是说存在f[i,k]的走法,1<=k<j
交换甲乙,则肯定有f[j,i]=f[i,j]。
目标结果
由于题中告知必须走到终点才能返回,输出结果一定是max{f[i,N]}(i到N存在飞机航线)。如果没有经过城市数目大于1的可行目标状态,则无法完成这次环游,输出1。
3.源码
#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include <cstring>
using namespace std;
const int MAXN=110;
map<string,int> flag;
bool relation[MAXN][MAXN];
int f[MAXN][MAXN];
int n,v;
ifstream fi("tour.in");
ofstream fo("tour.out");
void clearit()
{
memset(relation,0,sizeof(relation));
}
void init()
{
fi>>n>>v;
for(int i=1;i<=n;i++)
{
string tempname;
fi>>tempname;
flag[tempname]=i;
}
for(int i=1;i<=v;i++)
{
string name1,name2;
fi>>name1>>name2;
relation[flag[name1]][flag[name2]]=true;
relation[flag[name2]][flag[name1]]=true;
}
}
void doit()
{
int i,j,k;
f[1][1]=1;
for (i=1;i<=n;i++)
{
for (j=i+1;j<=n;j++)
{
f[i][j]=-1;
for (k=1;k<j;k++)
{
if ( f[i][k]>0 &&relation[k][j]&& f[i][k]>f[i][j])
f[i][j]=f[i][k];
}
f[j][i]=++f[i][j];
// j=j;
}
}
}
void outit()
{
int ans=1;
for(int i=1;i<n;i++)
if ((relation[i][n]) && (f[i][n]>ans))
ans=f[i][n];
fo<<ans<<endl;
}
int main()
{
clearit();
init();
doit();
outit();
fi.close();
fo.close();
return 0;
}