题目链接: https://codeforces.com/gym/100443/attachments
题意:
你现在有 节课要上,每节课 有一个上课区间 和这堂课的学生数量 ,每间教室最多可容纳 个学生,一节课 在结束后如果想要上另外一节课 ,那么需要等待 的时间(这里打扫完后还要有一分钟间隙)。现在问你最少需要借多少教室可以让所有的课都够用。
做法:
点的数量这么少的话就很明显是网络流了… 这里主要介绍两种做法(队友的做法和我的做法…)
先说我的做法吧,网上好像比较多的都是这样的。因为最大流求的是最大而不是最小,所以我们先把所有教室都分出来,然后用网络流求有多少教室是可以共用的,即总量-最大共用数,连接 流量均为 ,前一个表示去借,后一个表示总是能用上的,如果课程 上完可以赶上 ,那么 连接流量 ,表示 用的教室可以给 用。 然后跑出来的最大值就是可以共用的。
队友的做法是用费用流+二分, 连接流量为 ,费用为 的边, 连接流量为 费用为 的边,表示优先跑过这些边,然后二分 的流量, 在这里才是超级源点,如果跑出来的费用是 ,则这个流量是合法的,不断找更小值。
但是费用流因为需要二分,所以每次都要重新建图,所以其实时间应该是更久的(可能也有不完全需要重新建图的,但好像 暂时想不到)
代码
最大流做法
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
const int maxn=205;
const int maxm=200005;
const int MAX = 1<<26;
typedef long long ll;
typedef pair<int,int> pii;
int nex[maxm],to[maxm],cap[maxm],from[maxm];
int n,m,cnt,head[maxn],d[maxn],sp,tp;//原点,汇点
int wa[maxn][maxn],M;
struct node{
int l,r,need,id;
}no[maxn];
bool cmp(node a,node b){
return a.r<b.r;
}
//理论复杂度n2*m
void add(int u,int v,int c){
from[cnt]=u,to[cnt]=v,cap[cnt]=c,nex[cnt]=head[u],head[u]=cnt++;
from[cnt]=v,to[cnt]=u,cap[cnt]=0,nex[cnt]=head[v],head[v]=cnt++;
}
int bfs(){
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=nex[i]){
int u=to[i];
if(d[u]==-1 && cap[i]>0){
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp] != -1;
}
int dfs(int a,int b){
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=nex[i])
{
int u=to[i];
if(cap[i]>0 && d[u]==d[a]+1)
{
int x=min(cap[i],b-r);
x=dfs(u,x);
r+=x;
cap[i]-=x;
cap[i^1]+=x;
}
}
if(!r)d[a]=-2;
return r;
}
int dinic(int sp,int tp){
int total=0,t;
while(bfs()){
while(t=dfs(sp,MAX))
total+=t;
}
return total;
}
int main(){
int T,cas=0; scanf("%d",&T);
while(T--){
cnt=0; memset(head,-1,sizeof(head));
sp=0,tp=201;
scanf("%d%d",&n,&M);
int sum=0;
rep(i,1,n){
int S; scanf("%d%d%d",&no[i].l,&no[i].r,&S);
no[i].need=(S+M-1)/M; no[i].id=i;
sum+=no[i].need;
}
rep(i,1,n){
rep(j,1,n){
scanf("%d",&wa[i][j]);
}
}
sort(no+1,no+1+n,cmp);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(no[i].r+wa[no[i].id][no[j].id]<no[j].l){
add(i,j+n,MAX);
}
}
}
for(int i=1;i<=n;i++){
add(sp,i,no[i].need);
add(i+n,tp,no[i].need);
}
printf("Case %d: %d\n",++cas,sum-dinic(sp,tp));
}
return 0;
}
费用流做法
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=205;
const int maxm=200005;
const int inf=0x3f3f3f3f;
const ll finf=-1000;
int dis[maxn],sum;
int vis[maxn],pre[maxn];
int head[maxn],cnt,wa[maxn][maxn];
int n,m,M,k,sp,tp,can[maxn][maxn];
struct node{
int l,r,need,id;
}no[maxn];
bool cmp(node a,node b){
return a.r<b.r;
}
ll ans=0;
struct edge{
int to,cap,cost,next;
}e[maxm];
void add(int from,int to,int cap,int cost){
e[cnt].to=to; e[cnt].cap=cap;
e[cnt].cost=cost; e[cnt].next=head[from];
head[from]=cnt++;
e[cnt].to=from; e[cnt].cap=0;
e[cnt].cost=-cost; e[cnt].next=head[to];
head[to]=cnt++;
}
bool spfa(int s,int t,int &flow,ll &cost){
queue<int> q;
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
dis[s]=0; q.push(s);
vis[s]=1;
int d=inf;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(e[i].cap>0&&dis[v]>dis[u]+e[i].cost){
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
if(dis[t]==inf){
return false;
}
for(int i=pre[t];~i;i=pre[e[i^1].to]){
d=min(d,e[i].cap);
}
for(int i=pre[t];~i;i=pre[e[i^1].to]){
e[i].cap-=d;
e[i^1].cap+=d;
cost+=(ll)e[i].cost*d;
}
flow+=d;
return true;
}
ll mcmf(int s,int t){
int flow=0; ll cost=0;
while(spfa(s,t,flow,cost)){
//cout<<flow<<" "<<cost<<endl;
}
return cost;
}
int ck(int flo){
memset(head,-1,sizeof(head));
cnt=0;
add(sp,tp+1,flo,0);
for(int i=1;i<=n;i++){
add(tp+1,i,inf,0);
add(i,i+n,no[i].need,finf);
add(i+n,tp,inf,0);
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(can[i][j]) add(i+n,j,inf,0);
}
}
ll tmp=mcmf(sp,tp);
return tmp==finf*sum;
}
int main(){
int T,cas=0; scanf("%d",&T);
while(T--){
memset(can,0,sizeof(can));
ans=0;
sp=0;tp=201;
scanf("%d%d",&n,&M);
int L=0,R=0;
rep(i,1,n){
int S; scanf("%d%d%d",&no[i].l,&no[i].r,&S);
no[i].need=(S+M-1)/M; no[i].id=i;
R+=no[i].need;
}
sum=R;
rep(i,1,n){
rep(j,1,n){
scanf("%d",&wa[i][j]);
}
}
sort(no+1,no+1+n,cmp);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(no[i].r+wa[no[i].id][no[j].id]<no[j].l) can[i][j]=1;
}
}
int ans=R;
while(L<=R){
int mid=(L+R)/2;
if(ck(mid)){
ans=mid;
R=mid-1;
}
else L=mid+1;
}
printf("Case %d: %d\n",++cas,ans);
}
return 0;
}