題目鏈接: 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;
}