Canada Tour

题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate: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


解题思路:

  1. 参考NOCOW“动态规划”,见下
  2. 把返回的路线反向,那么整条路线就变成了两条不相交的从起点到终点的路线。这样我们可以用DP解决

  3. 状态设定:f[i,j] 为假定的甲乙两人,甲走到第i个城市,乙走到第j个城市时,两人走过的城市数目的和

  4. 初始状态:f[1,1]=1

  5. 状态转移方程:f[j,i]=f[i,j]=max{f[i,k]+1}(k到j存在飞机航线,以及f[i,k]>0,就是说存在f[i,k]的走法,1<=k<j

  6. 目标结果:由于题中告知必须走到终点才能返回,输出结果一定是max{f[i,N]}(i到N存在飞机航线)。 如果没有经过城市数目大于1的可行目标状态,则无法完成这次环游,输出1

代码

/*
ID: zc.rene1
LANG: C
PROG: tour
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_LEN 15
#define MAX_N 100

int N, V;
char list[MAX_N + 1][MAX_LEN + 1];
int table[MAX_N + 1][MAX_N + 1];
int f[MAX_N + 1][MAX_N + 1];

int GetIndex(char *s)
{
    int i;

    for (i=1; i<=N; i++)
    {
	if (strcmp(list[i], s) == 0)
	{
	    return i;
	}
    }

    return -1;
}

void GetInput(FILE *fin)
{
    int i, index1, index2;
    char s1[MAX_LEN + 1], s2[MAX_LEN + 1];


    fscanf(fin, "%d %d\n", &N, &V);
    
    for (i=1; i<=N; i++)
    {
	fscanf(fin, "%s\n", list[i]);
    }

    memset(table, 0, (MAX_N + 1) * (MAX_N + 1) * sizeof(int));

    for (i=1; i<=V; i++)
    {
	fscanf(fin, "%s %s\n", s1, s2);
	index1 = GetIndex(s1);
	index2 = GetIndex(s2);
	table[index1][index2] = 1;
	table[index2][index1] = 1;
    }
}

int DP(void)
{
    int i, j, k, max;

    memset(f, 0, (MAX_N + 1) * (MAX_N + 1) * sizeof(int));
    f[1][1] = 1;

    for (i=1; i<=N; i++)
    {
	for (j=i+1; j<=N; j++)
	{
	    max = -1;
	    for (k=1; k<j; k++)
	    {
		if (f[i][k] > 0 && table[k][j] == 1)
		{
		    if (f[i][k] + 1 > max)
		    {
			max = f[i][k] + 1;
		    }
		}
	    }
	    f[i][j] = max;
	    f[j][i] = max;
	}
    }
    

    max = 1;
    for (i=1; i<=N; i++)
    {
	if (table[i][N] == 1)
	{
	    if (f[i][N] > max)
	    {
		max = f[i][N];
	    }
	}
    }
    return max;
}

int main(void)
{
    FILE *fin, *fout;

    fin = fopen("tour.in", "r");
    fout = fopen("tour.out", "w");

    GetInput(fin);
    fprintf(fout, "%d\n", DP());

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值