本题在比赛的时候,没有深刻思考,直接写了一个DFS想要过掉,却TLE了整场。看来100的数据范围也不可小视!要明白这是100*100还是100^100,如果是后者,即加了剪枝,也不能起到太大的效果。赛后讨论看来,利用DFS是没错的。但是要运用好搜索树的特性,用DFS遍历整个搜索空间,从根依次向上返回判别值,对于已经计算出来的结果及时保存,避免重复计算,则可以大大地改善效率!
题意概述:给你N个点,M条边以及每条无向边的权值。并给出一个旅行序列,长度为tot。要求从0开始,边权严格按给出顺序(可以重复地)行走,最终到达终点n-1.
问在所有可行的路线上可能经过的点的总数。
解法:定义dp[i][j]表示到第i个点时已经走了j条边的可能性{0,1}。通过DFS,从根向上更新满足的情况。最终对于dp[n-1][tot]进行判断,若dp[n-1][tot]=1则说明存在可行路线,对每个i,j进行判断,将i加入待统计点集。否则输出0。
/*
Auther:Fs302
Prob:UVALive 4061 Photographic Tour
Time:20110814
Algorithm:
DP+DFS-->Memery Search
*/
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <vector>
#define N 105
using namespace std;
struct node
{
int v,cost;
};
vector<node> adj[101];
int n,m,tot;
int c[101],dp[101][101],vis[N];
int dfs(int i,int j)
{
int k,p,ok = 0;
if (dp[i][j]!=-1)
return dp[i][j];
if (j >= tot)
{
if (i == n-1)
{
return dp[i][j] = 1;
}
return 0;
}
for(k = 0;k < adj[i].size();k++)
{
p = adj[i][k].v;
if (adj[i][k].cost == c[j+1])
{
if (dfs(p,j+1)==1)
ok = 1;
}
}
return dp[i][j] = ok;
}
int main()
{
int i,j,k,x,y,z,cas = 0;
while(scanf("%d%d",&n,&m))
{
if (n==0 && m==0)
break;
memset(adj,0,sizeof(adj));
memset(c,0,sizeof(c));
memset(dp,-1,sizeof(dp));
memset(vis,0,sizeof(vis));
for(i = 1;i <= m;i++)
{
scanf("%d%d%d",&x,&y,&z);
node temp;
temp.v = y;
temp.cost = z;
adj[x].push_back(temp);
temp.v = x;
adj[y].push_back(temp);
}
scanf("%d",&tot);
for(i = 1;i <= tot;i++)
scanf("%d",&c[i]);
dp[0][0] = 1;
for(i = 0;i < adj[0].size();i++)
{
j = adj[0][i].v;
k = adj[0][i].cost;
if (k == c[1])
{
dfs(j,1);
}
}
if (dp[n-1][tot] == 1)
{
int count=0;
for(i = 0;i <= n-1;i++)
{
for(j = 0;j <= tot;j++)
{
if (dp[i][j] == 1)
{
if (vis[i]==0)
{
vis[i] = 1;
count++;
}
}
}
}
printf("Tour %d: %d\n",++cas,count);
}
else
printf("Tour %d: 0\n",++cas);
}
return 0;
}