题意:
给你N个点,M条双向边,L个饭店,S代表起点,T代表时间限制
给你M个a,b,c 表示走过边(a,b)的花费为c
给你L个j,e 表示点j'旁边有个饭店,在该饭店吃饭需要时间e
-
2 ≤ n ≤ 300
-
1 ≤ m ≤ 5,000
-
1 ≤ l ≤ 16
-
1 ≤ s ≤ n
-
1 ≤ t ≤ 100,000
-
1 ≤ ai, bi ≤ n
-
1 ≤ ci ≤ 1,000
-
1 ≤ ji ≤ n
-
1 ≤ ei ≤ 15
想法: 训练赛的时候我就想到先用弗洛伊德预处理,然后就把图变成了一个只有16个点的图,后面我想用状态压缩,也想过用dp,当时以为这不能用dp,因为dp有后效性
后面自己用dfs写TLE了
比赛之后HNU的学长在群里说记忆化搜索就是dp,我肯定是因为没有记忆化。。。
等他将完这题的时候我才知道。。 原来状态还可以酱紫转移的。。 涨姿势了。。
PS: 写代码写了好久才写出来。。。 真是无语。。 知道怎么做居然写不出来。。 LCB 教我用递归写了一次。。 昨晚还是TLE了。。 今早上用for循环写AC了。。
代码:
#include <stdio.h>
#include <string.h>
#define maxn 305
#define maxt 100005
#define REP(i,n) for(int i= 1; i<= (n); i++)
int map[maxn][maxn];
int dis[maxn][maxn];
int n, m, l, s, t;
int pos[20], c[20];
int dp[1<<17][20];
int cnt[1<<17][20];
int ans;
int min(int x, int y)
{
return x< y? x: y;
}
void Floyd()
{
REP(i, n) REP(j, n)
dis[i][j]= map[i][j];
REP(u, n) REP(v, n) REP(w, n)
if(dis[u][v] + dis[u][w] < dis[v][w])
dis[v][w]= dis[u][v] + dis[u][w];
}
/*
void get(int ss, int k)
{
//printf("%d %d %d %d %d %d\n",ss,k,pos[k],dp[ss][k], dis[s][pos[k]],dp[ss][k]+ dis[ss][pos[k]]);
if(dp[ss][k]+ dis[s][pos[k]]> t)
return;
// printf("xx\n");
if(cnt[ss][k]> ans)
ans= cnt[ss][k];
for(int i= 1; i<= l; i++)
if(ss&(1<<i))continue;
else
{
int xx= dp[ss][k] + dis[pos[k]][pos[i]] + c[i];
// printf("%d %d %d %d %d %d\n",xx,dp[s][k], dis[pos[k]][pos[i]],c[i],pos[k],pos[i]);
int hh= ss|(1<<i);
if(dp[hh][i]== -1)
dp[hh][i]= xx;
else
dp[hh][i]= min(dp[hh][i], xx);
cnt[hh][i]= cnt[ss][k] + 1;
get(hh, i);
}
}
*/ //之前的递归代码
int main()
{
while(scanf("%d %d %d %d %d",&n,&m,&l,&s,&t)!=EOF)
{
if(n+m+l+s+t==0)
break;
REP(i, n) REP(j, n)
map[i][j]= maxt;
for(int i= 1; i<= m; i++)
{
int a, b, c;
scanf("%d %d %d",&a,&b,&c);
map[a][b]= map[b][a]= c;
}
for(int i= 1; i<= l; i++)
scanf("%d %d",&pos[i], &c[i]);
pos[0]= s;
Floyd();
memset(dp, -1, sizeof(dp));
memset(cnt, 0, sizeof(cnt));
dp[1][0]= 0;
dis[s][s]= 0;
ans= 0;
for(int num= 1; num< (1<<(l+1)); num+=2) //用一个L+1位二进制数表示,其中最低位表示起点S
for(int i= 1; i<= l; i++)
if(num&(1<<i))continue;
else
{
for(int k= 0; k<= l; k++)
if(num&(1<<k) && dp[num][k]!=-1) // 这里要判断dp[num][k]是否非法 因为S点只能在第一步往其他点延伸
{
int xx= dp[num][k] + dis[pos[k]][pos[i]] + c[i];
int hh= num|(1<<i);
if(dp[hh][i]== -1)
dp[hh][i]= xx;
else
dp[hh][i]= min(dp[hh][i], xx);
cnt[hh][i]= cnt[num][k] + 1;
}
}
for(int i= 1; i< (1<< (l+1)); i++)
for(int j= 1; j<= l; j++)
{
if(dp[i][j]!=-1 && dp[i][j] + dis[s][pos[j]]<= t && cnt[i][j]> ans)
ans= cnt[i][j];
}
printf("%d\n",ans);
}
return 0;
}