题面

传送门

思路

先看看这道题

修车

仔细理解一下,这两道题是不是一样的?

这道题的不同之处

但是有一个区别:本题中每一种车有多个需求,但是这个好办,连边的时候容量涨成$p\lbrack i\rbrack$就好了

但是还有一个区别:数据量变大了-_-

这直接导致了费用流裸做,TLE60分,因为有超过6e6条边

我们得想个办法改进一下

观察可得,这道题里,按照我们的模型,最多出现800条增广路,而且每次增广都是一的流量

也就是说我们实际上跑800次spfa即可

但是spfa和边唯一相关,我们全建好的图中6e6*800*k肯定会T

那我们就要想个办法优化边数

优化

我们观察发现,第一次spfa得出的最短路肯定是某人倒数第一个修某车某厨师倒数第一个做某菜,因为倒数第一个肯定比倒数第二个距离短

那么我们可以在一开始建图的时候,只把所有“倒数第一个做的菜”的那些边加上

一旦一条增广路被用掉了(也就是一个厨师-做菜顺序二元组$\left(j,k\right)$被用掉了),那么我们就把所有代表二元组$\left(j,k+1\right)$加上去(一共有n条),再跑spfa

这样我们图中的总边数不会超过$n\ast\sum_{i=1}^n p\lbrack i\rbrack$

也就是总时间在$O\left(np^2\ast k\right)$左右,k是spfa常数

这样就可以过了

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 1e9
#define id(i,j) ((i-1)*p+j+n)
#define left(x) ((x-n-1)/p+1)
#define right(x) ((x-n-1)%p+1)
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,m,cnt=-1,first[100010],dis[100010],vis[100010],pre[100010],limit[100010];
struct edge{
int to,next,w,cap;
}a[10000010];
inline void add(int u,int v,int w,int cap){
a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
int q[200010],ans,cost[50][110],p;
bool spfa(int s,int t){
int head=0,tail=1,u,v,w,i;
memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));memset(limit,0,sizeof(limit));
q[0]=s;vis[s]=1;dis[s]=0;limit[s]=inf;
while(head<tail){
u=q[head++];vis[u]=0;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;w=a[i].w;
if(a[i].cap&&((dis[v]==-1)||(dis[v]>dis[u]+w))){
dis[v]=dis[u]+w;
pre[v]=i;limit[v]=min(limit[u],a[i].cap);
if(!vis[v]) q[tail++]=v,vis[v]=1;
}
}
}
return ~dis[t];
}
void mcmf(int s,int t){
int u,i;
while(spfa(s,t)){//这里最多sigma(p[i])次
for(u=t;~pre[u];u=a[pre[u]^1].to){
a[pre[u]].cap-=limit[t];a[pre[u]^1].cap+=limit[t];
ans+=limit[t]*a[pre[u]].w;
}//跑完一次更新答案
u=a[pre[t]^1].to;//u就是当前消耗的二元组,u+1就是下一个二元组
add(u+1,t,0,1);
for(i=1;i<=n;i++) add(i,u+1,cost[i][left(u+1)]*right(u+1),1);//加上对应的下一组边
}
}
int main(){
memset(first,-1,sizeof(first));int i,j,t1;
n=read();m=read();
for(i=1;i<=n;i++){
t1=read();p+=t1;
add(0,i,0,t1);
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cost[i][j]=read();
add(i,id(j,1),cost[i][j],1);//初始边
}
}
for(j=1;j<=m;j++) add(id(j,1),n+p*m+1,0,1);
mcmf(0,n+p*m+1);
cout<<ans<<endl;
}

最新文章

  1. 子DIV设置margin-top影响父DIV位置的解决办法
  2. 第三讲:WCF介绍(3)
  3. 使用js制作一般网站首页图片轮播效果
  4. IE11兼容IE9问题
  5. (转载)AS3.0实例学习 熟悉新的事件机制和addChild的运用
  6. 【转】一个FAE(AE)的体会和大家交流
  7. MySql按指定天数进行分组数据统计分析 1
  8. PhpMyAdmin隐藏数据库设置同前缀失效的问题
  9. yum网络源配置
  10. python--DenyHttp项目(2)--ACM监考服务器端
  11. Eureka的工作原理以及它与ZooKeeper的区别
  12. C 语言 IO 缓存 相关
  13. 移动端input“输入框”常见问题及解决方法
  14. web plugins
  15. Kali学习笔记11:僵尸扫描案例
  16. Git(管理修改)
  17. 转-OWASP CSRFGuard使用细节
  18. Nginx安装使用及与tomcat实现负载均衡
  19. Visual Studio自带的的Developer Command Prompt对话框
  20. 8.1 服务器开发 API 函数封装,select 优化服务器和客户端

热门文章

  1. 2018.6.16 PHP小实验
  2. python剑指offer剪绳子
  3. SecureCRT连接Linux
  4. vue2.0父子组件以及非父子组件通信
  5. AngularJS最佳实践
  6. A1043 Is It a Binary Search Tree (25 分)
  7. 【计数】cf223C. Partial Sums
  8. 两种方法实现text输入框中“请输入关键字”的提醒
  9. js控制台输出图案
  10. Python学习笔记(七)加密加盐