[APIO2017]商旅
这题卡我精度,调了一晚上才调对,因为没有想到图还可以不连通
其实可以预处理出好多东西,距离($dis(u,v)$),买卖物品(从$u$到$v$买卖物品的最大利润,例($max{S_{u,i}-B_{v,i}}$),然后其实可以发现就是一个十分普通普遍的分数规划式子,就每次二分$k$,然后建边,$spfa$判正环就行
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<queue> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1001; int n,m,k; int dis[MAXN][MAXN],b[MAXN][MAXN],s[MAXN][MAXN],st[MAXN][MAXN],l,r,maxn,D[MAXN][MAXN],num[MAXN],Dis[MAXN],vis[MAXN]; queue<int> que; bool check(int P){ while(!que.empty()) que.pop(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) D[i][j]=st[i][j]-dis[i][j]*P; memset(Dis,0,sizeof(Dis)); memset(num,0,sizeof(num)); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) num[i]=vis[i]=1,que.push(i); while(!que.empty()){ int xx=que.front();que.pop();vis[xx]=0; for(int i=1;i<=n;i++){ int d=D[xx][i]; if(dis[xx][i]>INT_MAX) continue; if(Dis[xx]+d>=Dis[i]){ Dis[i]=d+Dis[xx]; num[i]=num[xx]+1; if(num[i]>n+10) return 1; if(!vis[i]){ vis[i]=1; que.push(i); } } } }return 0; } signed main(){ memset(dis,127/3,sizeof(dis)); n=read(),m=read(),k=read(); for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) b[i][j]=read(),s[i][j]=read(); for(int i=1;i<=k;i++){ for(int x=1;x<=n;x++) for(int y=1;y<=n;y++) if(b[x][i]!=-1&&s[y][i]!=-1) { st[x][y]=max(st[x][y],s[y][i]-b[x][i]),r=max(r,st[x][y]); } } for(int i=1;i<=m;i++){ int u=read(),v=read(),w=read(); dis[u][v]=min(dis[u][v],w); } for(int ss=1;ss<=n;ss++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][ss]+dis[ss][j]); while(l<=r){ int mid=l+r>>1; if(check(mid)) maxn=max(maxn,mid),l=mid+1; else r=mid-1; } printf("%lld\n",maxn); }