P2402
有 个点和 条有边权无向边,对于每个点有牛的数量 和牛棚容量 。
求问所有牛全部进入牛棚所需的最小时间,无解输出 -1。
。
看到 ,可以想到用 Floyd 跑一遍全源最短路。
int edge[N][N];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
edge[i][j]=(i==j?0:INF);
for(int i=1,u,v,w;i<=m;i++){
u=rd(),v=rd(),w=rd();
edge[u][v]=min(edge[u][v],w);
edge[v][u]=min(edge[v][u],w);
}for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
edge[i][j]=min(edge[i][j],edge[i][k]+edge[k][j]);
看到求最少时间,考虑二分答案。
while(l<=r){
mid=(l+r)>>1;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}wr(ans?ans:-1);
看到“牛进入牛棚”这样的描述,很容易想到二分图匹配。
网络流的建边方法:
- 首先将每个点分成牛和牛棚两个点。
- 从源点向每个表示牛的点连一条流量为目标点的牛的数量的边。
- 从每个表示牛棚的点向汇点连一条流量为当前点牛棚容量的边。
- 如果两点之间距离小于当前的时间,就连一条流量为两点中表示牛的点的牛的数量的边。
for(int i=1;i<=n;i++) I(st,i,s[i]);
for(int i=1;i<=n;i++) I(i+n,ed,p[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(edge[i][j]<=lim)
I(i,j+n,s[i]);
其实建边的方法就是二分图多重最大权匹配的板子。
建边后直接跑网络流板子即可。
几个细节点:
- 二分答案的右边界赋值一定要小于 floyd 的边权初始极值。
- 因为有拆点,所以数组一定要开两倍空间。
- 注意题目中的边都是无向边。
- 存在重边,邻接矩阵取两点之间距离最小值。
- 不开 long long 见祖宗。
完整代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=600,M=2e5,INF=3e18;
int rd(){
int w=0,v=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')v=-1;c=getchar();}
while(c>='0'&&c<='9')w=(w<<1)+(w<<3)+(c&15),c=getchar();return w*v;
}void wr(int x){if(x<0)putchar('-'),x=-x;if(x>9)wr(x/10);putchar(x%10+'0');}
int fir[N],cnt=1;
struct E{int v,w,nt;}e[M];
void I(int u,int v,int w){
e[++cnt]=(E){v,w,fir[u]};fir[u]=cnt;
e[++cnt]=(E){u,0,fir[v]};fir[v]=cnt;
}void cl(){memset(e,0,sizeof(e));memset(fir,0,sizeof(fir));cnt=1;}
int cur[N],d[N],st,ed;
queue <int>q;
bool bfs(){
memset(d,0,sizeof(d));
for(int i=0;i<=ed;i++) cur[i]=fir[i];
q.push(st);d[st]=1;
while(q.size()){
int u=q.front(),V;q.pop();
for(int i=fir[u];i;i=e[i].nt)
if(e[i].w&&!d[V=e[i].v])
q.push(V),d[V]=d[u]+1;
}return d[ed];
}int dfs(int u,int fl){
if(u==ed)return fl;int f,V,s=0;
for(int i=cur[u];i;i=e[i].nt){
cur[u]=i;
if(e[i].w&&d[V=e[i].v]==d[u]+1){
f=dfs(V,min(fl,e[i].w));
e[i].w-=f;e[i^1].w+=f;
s+=f;fl-=f;if(!fl)break;
}
}if(!s)d[u]=0;return s;
}int dinic(){int ans=0;while(bfs())ans+=dfs(st,INF);return ans;}
int n,m,ans,s[N],p[N];
int s1,s2;
int l=1,r=INF-1,mid;
int edge[N][N];
void floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
edge[i][j]=min(edge[i][j],edge[i][k]+edge[k][j]);
}
bool check(int lim){
cl();
for(int i=1;i<=n;i++) I(st,i,s[i]);
for(int i=1;i<=n;i++) I(i+n,ed,p[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(edge[i][j]<=lim)
I(i,j+n,s[i]);
return (dinic()==s1)?1:0;
}
signed main(){
n=rd(),m=rd();st=0,ed=n*2+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
edge[i][j]=(i==j?0:INF);
for(int i=1;i<=n;i++){
s[i]=rd(),p[i]=rd();
s1+=s[i];s2+=p[i];
}for(int i=1,u,v,w;i<=m;i++){
u=rd(),v=rd(),w=rd();
edge[u][v]=min(edge[u][v],w);
edge[v][u]=min(edge[v][u],w);
}floyd();
if(s1>s2){wr(-1);return 0;}
while(l<=r){
mid=(l+r)>>1;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}wr(ans?ans:-1);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)