P1772 [ZJOI2006]物流运输
dp套spfa是最骚的qwq
首先要说明一点:这道题数据比较小,允许你用你觉得似乎过不了的复杂度过!
总体思路是这样的:
我们定义一个sp[i][j]
数组表示第\(i\)天到第\(j\)天(闭区间)内的最短路,再定义dp[i]
为前\(i\)天的最小代价。
那么就可以列出这么个柿子:
\[dp[i]=max_{j=1}^{i-1}dp[j]+sp[j+1][i]+k
\]
在递推之前可以初始化\(dp[i]=sp[1][i]\)。
求\(20^2=400\)次\(O(nm)\)spfa,随便写都保证不会T啦。。。
然后你就可以美滋滋地A了。
但我这个菜鸡还想了好久还不会
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
const int maxn = 25, maxt = 105;
const int INF = 0x3f3f3f3f;
struct Edges
{
int next, to, weight;
} e[1005];
int head[maxn], tot;
int dist[maxn];
bool block[maxn][maxt];
bool vis[maxn];
ll sp[maxt][maxt];
bool exist[maxn];
ll dp[maxt];
int n, m, t, cost;
void link(int u, int v, int w)
{
e[++tot] = (Edges){head[u], v, w};
head[u] = tot;
}
int spfa(int x, int y)
{
//memset(vis, false, sizeof vis);
memset(dist, 0x3f, sizeof dist);
memset(exist, true, sizeof exist);
for(int i = 1; i <= n; i++)
{
for(int j = x; j <= y; j++)
{
if(block[i][j])
{
exist[i] = false;
break;
}
}
}
std::queue<int> q;
dist[1] = 0; q.push(1); vis[1] = true;
while(!q.empty())
{
int u = q.front(); q.pop(); vis[u] = false;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(!exist[v]) continue;
if(dist[u] + e[i].weight < dist[v])
{
dist[v] = dist[u] + e[i].weight;
if(!vis[v])
{
q.push(v); vis[v] = true;
}
}
}
}
return dist[n];
}
int main()
{
scanf("%d%d%d%d", &t, &n, &cost, &m);
while(m--)
{
int u, v, w; scanf("%d%d%d", &u, &v, &w);
link(u, v, w); link(v, u, w);
}
scanf("%d", &m);
while(m--)
{
int p, x, y; scanf("%d%d%d", &p, &x, &y);
for(int i = x; i <= y; i++) block[p][i] = true;
}
for(int i = 1; i <= t; i++)
{
for(int j = i; j <= t; j++)
{
sp[i][j] = spfa(i, j);
//printf("sb: %d\n", sp[i][j]);
}
}
for(int i = 1; i <= t; i++)
{
dp[i] = sp[1][i] * i;
for(int j = 1; j < i; j++)
{
if(sp[j + 1][i] != INF) dp[i] = std::min(dp[i], dp[j] + (i - j) * sp[j + 1][i] + cost);
//printf("sb: %lld\n", dp[i]);
}
}
printf("%lld\n", dp[t]);
return 0;
}