[CCO2015] 路短最
题目
Description
你可以通过许多的算法找到从一个地方到另外一个地方的最短路径。人们在他们的车上安装 GPS 设备然后他们的手机告诉他们最快的到达目的地的方式。然而,当在假期时,Troy 喜欢慢慢旅游。他想找最长的到目的地的路径以便他可以在路途中看许多新的以及有趣的地方。
因此,一个有效的路径包含一个不同城市的序列 c1,c2,...,ckc1,c2,...,ck,并且对于每个 1≤i<k1≤i<k,有道路从 cici 通往 ci+1ci+1。
他不想重复访问任何城市,请帮他找出最长路径。
Input
第一行输入包括两个整数 n,mn,m,分别表示城市总数和连接城市间的道路数,两城市间至多有一条道路。城市编号从 00 到 n−1n−1,Troy 一开始在城市 00,城市 n−1n−1 是他的目的地。
接下来 mm 行每行三个整数 s,d,ls,d,l,每个三元组表示这里有一条长为 ll 的从城市 ss 到城市 dd 的路。每条路都是有向的,只能从 ss 到 dd,不能反向。保证有一条从城市 00 到 n−1n−1 的路径。
Output
输出一个整数表示以城市 00 为起点,以 n−1n−1 为终点的最长路径长度,并且其中不重复访问城市,路径长度是所经过的道路长度之和。
Sample Input
3 3 0 2 5 0 1 4 1 2 3
Sample Output
7
思路
这是一道状压$dp$的经典题
那么状态转移方程很显然就是:
$dp[i][k]=min(dp[i][k],dp[j][k \oplus (1<<(i-1))]+a[j][i]);$
$k$ 是枚举的一个集合,$i$ 和 $j$ 都是枚举当前到达的城市;
代码
#include<bits/stdc++.h> typedef long long ll; using namespace std; ll n,m; ll a[20][20],dp[20][1<<19]; int main() { memset(dp,-127/3,sizeof(dp));//初始化 scanf("%lld%lld",&n,&m); for(ll i=1;i<=m;i++) { ll x,y,z; scanf("%lld%lld%lld",&x,&y,&z); a[x+1][y+1]=z; } dp[1][1]=0;//只能从第一个城市出发 for(ll k=1;k<=(1<<n)-1;k+=2) for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++) if(k&(1<<(i-1))&&k&(1<<(j-1))&&a[j][i])//有路才转移 { dp[i][k]=max(dp[i][k],dp[j][k^(1<<(i-1))]+a[j][i]); } ll ans=0; for(ll i=1+(1<<(n-1));i<=(1<<n)-1;i+=2)//枚举包含第一个和最后一个城市的路径 ans=max(ans,dp[n][i]); printf("%lld",ans); return 0; }