题目
略,luogu上有
解析
一眼费用流
然而怎么建图?
首先我们要挖掘题中的限制条件和性质
- 一个点只能经过一次
- 能走的天数最长
- 满足第二条的条件下走过的路程最短
那么显然是最小费用最大流了
对于后两条,我们发现我们求的最大流就要对应天数,最小费用就要对应路程最短
再联系第一条
一个点只能经过一次,那么我们就可以把它拆开成两个点 (i_1,i_2) 连流量为 (1) 费用为 (0) 的边,表示只能流过一次
然后就要使 (u,v) 间的连边变为 (u_2,v_1,1,w) 和 (v_1,u_2,0,-w)
那么源点 (S) 就是 (1) 拆成的第二个点,汇点 (T) 就是 (n_1)
最后跑一边 (MCMF) 就好了
(Code)
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 405 , M = 4e4 + 5;
int n , m , pre[N] , edge[N] , dis[N] , flow[N] , h[N] , vis[N] , tot = 1 , Mincost , Maxflow , S , T;
queue<int> Q;
struct node{
int to , nxt , w , f;
}e[M << 1];
inline void add(int u , int v , int w , int f){e[++tot] = node{v , h[u] , w , f} , h[u] = tot;}
inline int spfa()
{
memset(dis , 127 , sizeof dis);
memset(flow , 127 , sizeof flow);
memset(vis , 0 , sizeof vis);
dis[S] = 0 , vis[S] = 1 , Q.push(S) , pre[T] = -1;
while (!Q.empty())
{
int now = Q.front();
Q.pop();
for(register int i = h[now]; i; i = e[i].nxt)
{
int v = e[i].to;
if (dis[now] + e[i].f < dis[v] && e[i].w)
{
dis[v] = dis[now] + e[i].f , pre[v] = now , edge[v] = i , flow[v] = min(flow[now] , e[i].w);
if (!vis[v]) vis[v] = 1 , Q.push(v);
}
}
vis[now] = 0;
}
return pre[T] != -1;
}
inline void MCMF()
{
while (spfa())
{
Maxflow += flow[T];
Mincost += dis[T] * flow[T];
int now = T;
while (now != S)
{
e[edge[now]].w -= flow[T];
e[edge[now] ^ 1].w += flow[T];
now = pre[now];
}
}
}
int main()
{
scanf("%d%d" , &n , &m);
for(register int i = 1; i <= n; i++)
{
add(i * 2 - 1 , i * 2 , 1 , 0);
add(i * 2 , i * 2 - 1 , 0 , 0);
}
int u , v , f;
for(register int i = 1; i <= m; i++)
{
scanf("%d%d%d" , &u , &v , &f);
add(u * 2 , v * 2 - 1 , 1 , f) , add(v * 2 - 1 , u * 2 , 0 , -f);
}
S = 2 , T = n * 2 - 1;
MCMF();
printf("%d %d
" , Maxflow , Mincost);
}