[SDOI2009]晨跑
原题连接:https://www.luogu.org/problemnew/show/P2153
题目描述
Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑、仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑。 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从 一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交。Elaxia每天从寝室出发 跑到学校,保证寝室编号为1,学校编号为N。 Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以 在一个周期内,每天的晨跑路线都不会相交(在十字路口处),寝室和学校不算十字路 口。Elaxia耐力不太好,他希望在一个周期内跑的路程尽量短,但是又希望训练周期包含的天 数尽量长。 除了练空手道,Elaxia其他时间都花在了学习和找MM上面,所有他想请你帮忙为他设计 一套满足他要求的晨跑计划。
存在1→n 的边存在。这种情况下,这条边只能走一次。
输入输出格式
输入格式:
第一行:两个数N,M。表示十字路口数和街道数。 接下来M行,每行3个数a,b,c,表示路口a和路口b之间有条长度为c的街道(单向)。
输出格式:
两个数,第一个数为最长周期的天数,第二个数为满足最长天数的条件下最短的路程长 度。
输入输出样例
7 10 1 2 1 1 3 1 2 4 1 3 4 1 4 5 1 4 6 1 2 5 5 3 6 6 5 7 1 6 7 1
输出样例
2 11
说明
对于30%的数据,N ≤ 20,M ≤ 120。
对于100%的数据,N ≤ 200,M ≤ 20000。
嗯....这道题目要求最短长度,最多天。那很显然跑最小费用最大流就可以了
讲一讲建边qwq
每个十字路口只能经过一次,既然这样,那我们就将每个十字路口拆成两个点 流量设为1,费用为0(为什么,想一想) a->a1(a1表示拆出来的点) qwq,但是源点,汇点不止走一次,所以不用拆点
Code
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N=20000+10; const int M=6000000; const int inf=0x7fffffff; int v[N*4],w[N*4],cst[N*4],nxt[N*4],first[N],dis[N],pre[N],pos[N]; int cnt=1,n,m,flow,cost,t; bool inq[N]; struct queue{ int head,tail,que[M]; void clear(){head=tail=1;} //这一行其实没有意义 void pop(){head++;if(head==M) head=0;} void push(int x){que[tail++]=x;if(tail==M) tail=0;} int front(){return que[head];} bool empty(){return head==tail;} }; queue q; //把queue封装在结构体里 void read(int &x){ //读入优化 x=0;int f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c-'0');c=getchar();} x*=f; } void add(int a,int b,int c,int d){ //前向星存边 v[++cnt]=b; w[cnt]=c; cst[cnt]=d; nxt[cnt]=first[a]; first[a]=cnt; } bool spfa(){ //找最小费用 for(int i=1;i<=2*n;i++) dis[i]=inf; q.clear(); q.push(1);dis[1]=0;inq[1]=1; while(!q.empty()){ int x=q.front();q.pop();inq[x]=0; for(int i=first[x];i;i=nxt[i]){ if(w[i]&&dis[x]+cst[i]<dis[v[i]]){ pre[v[i]]=x;pos[v[i]]=i; //pre:记录前继节点,pos:该节点对应的边 dis[v[i]]=dis[x]+cst[i]; if(!inq[v[i]]){ q.push(v[i]); inq[v[i]]=1; } } } } return dis[n]!=inf; //是否存在一条从源点到汇点的路径 } int main(){ read(n);read(m); for(int i=1;i<=m;i++){ int a,b,c; read(a);read(b);read(c); if(a!=1) a=n+a; //用拆出来的点连b,1不用拆点 add(a,b,1,c);add(b,a,0,-c); } for(int i=2;i<n;i++){add(i,i+n,1,0);add(i+n,i,0,0);} //拆点 while(spfa()){ int f=1; flow+=1;cost+=dis[n]; for(int i=n;i!=1;i=pre[i]){ w[pos[i]]-=f; w[pos[i]^1]+=f; } } printf("%d %d",flow,cost); return 0; }