有m条单向边连接了N个城市(1<=m,N<=10),走这些路径是要付费的,有两种付费方式,例如从 城市 ai 到 bi
1、在城市 ci 提前支付,(ci 可能等于 ai,可能不等于)
2、在到达 bi 后支付
第一种支付的金额为 P,第二种支付的金额为R。(P<=R)
求出从 1 到 N的最小花费。
定义DP数组,dp[S][v]代表已经走过的点为S,且当前在 v 的最小花费。
初始化整个数组为 无穷,dp[1][0]=0,代表已经走过 0号点,且在0号点的花费为0。
然后循环 大小为 1、2 ... N - 1 大小的集合。
执行如下递推式。
dp[S | 1 << u][u] = min(dp[S | 1 << u][u] , dp[S][v] + costVU)
其中costVU等于 v 到 u 需要计算,初值是 达到 u 之后付费的金额。
然后循环集合S 以内的点,挨个计算从这个点 提起支付 v 到 u 路径的金额,最后取一个最小。
注意事项:本题目是可以走回头路的,因为集合相同,但是终点不同。
例如测试用例中,从1到达2,再从2回到1,再从1到达3,是1到3的最便宜的路径(在2位置支付1到3的路径的金额)。
所以已走过的点可以再走。
另外,因为 从1达到 2,从2回到1,会更新 dp[0b11][0]的值,而按照集合大小循环,计算到dp[0b11][1]时,dp[0b11][0]的值已经循环过了,所以每个集合的大小要循环两次。
#include
using namespace std;
const int MAX_N = 10, INF = 0x3f3f3f3f;
int dp[1 << MAX_N][MAX_N], cost[MAX_N][MAX_N], preCost[MAX_N][MAX_N][MAX_N], N, all;
void handle(int S, int v, int u)
{
if (dp[S][v] == INF)
{
return;
}
int costVU = cost[v][u];
for (int i = 0; i < N; i++)
{
if (S >> i & 1)
{
costVU = min(costVU, preCost[i][v][u]);
}
}
if (costVU == INF)
{
return;
}
if (dp[S][v] + costVU < dp[S | 1 << u][u])
{
dp[S | 1 << u][u] = dp[S][v] + costVU;
}
}
void solveItem(int size)
{
int used = 0;
for (int i = 0; i < size; i++)
{
used = used | 1 << i;
}
while (used < all)
{
for (int v = 0; v < N; v++)
{
for (int u = 0; u < N; u++)
{
if (used >> v & 1)
{
handle(used, v, u);
}
}
}
int x = used & -used;
int y = used & ~(used + x);
used = used + x + (y / x / 2);
}
}
void doDp()
{
for (int i = 0; i < (1 << MAX_N); i++)
{
for (int j = 0; j < MAX_N; j++)
{
dp[i][j] = INF;
}
}
all = (1 << N) - 1;
dp[1][0] = 0;
for (int i = 1; i <= N - 1; i++)
{
solveItem(i);
solveItem(i);
}
int ans = INF;
for (int i = 1; i < (1 << N); i++)
{
ans = min(ans, dp[i][N - 1]);
}
if (ans == INF)
{
printf("impossible\n");
}
else
{
printf("%d\n", ans);
}
}
void input()
{
for (int i = 0; i < MAX_N; i++)
{
for (int j = 0; j < MAX_N; j++)
{
cost[i][j] = INF;
for (int k = 0; k < MAX_N; k++)
{
preCost[i][j][k] = INF;
}
}
}
for (int i = 0; i < MAX_N; i++)
{
cost[i][i] = 0;
for (int j = 0; j < MAX_N; j++)
{
preCost[i][j][j] = 0;
}
}
int m, a, b, c, P, R;
scanf("%d%d", &N, &m);
for (int i = 0; i < m; i++)
{
scanf("%d%d%d%d%d", &a, &b, &c, &P, &R);
a--;
b--;
c--;
preCost[c][a][b] = min(preCost[c][a][b], P);
cost[a][b] = min(cost[a][b], R);
}
}
int main()
{
input();
doDp();
return 0;
}