usaco 5.4.2 2011.2.26

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;

}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值