费用流
考虑这么建图:
①因为每个点只能访问一次,因此考虑进行拆点,把每个点拆成入点x与出点y(1和n除外),在x与y之间建一条容量为1,距离为0的边。
②对于从1开始的点,建一条1–>i.x容量为1的边。
③对于指向n的点,建一条i.y–>n容量为1的边。
④对于其他点u,v,建一条u.y–>v.x容量为1的边。
最后跑一遍费用流即可,最大流的答案即为天数,费用即为路程和。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 400
#define MAXM 40000
using namespace std;
struct edge{
int next,to,dis,v,flow;
};
struct father{
int x,e;
};
int n,m,k;
int h[MAXN+5],dis[MAXN+5],rem[MAXN+5],que[2*MAXM+5];
bool f[MAXN+5];
edge ed[2*MAXM+5];
father fa[MAXN+5];
int spfa(){
memset(f,false,sizeof(f));
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[1]=0; que[1]=1; rem[1]=0x7fffffff;
int r=0,w=1;
while (r<w){
int x=que[++r];
f[x]=false;
for (int i=h[x];~i;i=ed[i].next)
if (ed[i].v>ed[i].flow&&dis[ed[i].to]>dis[x]+ed[i].dis){
int v=ed[i].to;
dis[v]=dis[x]+ed[i].dis;
fa[v].x=x; fa[v].e=i;
rem[v]=min(rem[x],ed[i].v-ed[i].flow);
if (!f[v]){ f[v]=true; que[++w]=v; }
}
}
if (dis[n]==0x3f3f3f3f) return 0;
return rem[n];
}
void change(int sum){
int now=n;
while (now!=1){
int e=fa[now].e;
ed[e].flow+=sum; ed[e^1].flow-=sum; now=fa[now].x;
}
}
void mcmf(){
int ans=0,cst=0;
while (1){
int sum=spfa();
if (!sum) { printf("%d %d\n",ans,cst); return; }
ans++; cst+=sum*dis[n]; change(sum);
}
}
void addedge(int x,int y,int z,int w){
ed[k].next=h[x]; ed[k].to=y; ed[k].v=z; ed[k].dis=w; h[x]=k++;
ed[k].next=h[y]; ed[k].to=x; ed[k].v=0; ed[k].dis=-w; h[y]=k++;
}
int main(){
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
for (int i=1;i<=m;i++){
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
if (u!=1&&v!=n) addedge(u+n,v,1,d);
if (u==1&&v!=n) addedge(u,v,1,d);
if (u!=1&&v==n) addedge(u+n,v,1,d);
if (u==1&&v==n) addedge(u,v,1,d);
}
for (int i=2;i<n;i++)
addedge(i,i+n,1,0);
mcmf();
return 0;
}