題意:
有n位選手和m位導師,每位導師戰隊有容量限制 。
每位選手有一張志願表。對於每位選手,導師之間允許並列(最多允許C位導師並列)。所有選手有一個排名,選手之間不允許並列。
導師錄取的規則可以簡單概括爲:對每位選手,在優先滿足更高位選手的前提下,儘可能滿足該選手最高位志願。
每位選手有一個理想的志願s_i(夢想),表示他希望被s_i或更高位的志願錄取。
求:
1.每位選手的最終錄取情況。
2.每位選手至少要上升幾名,才能被理想的志願錄取(實現夢想)。
Solution:
考場上只想到了費用流,但是T掉了,自測是跑了3s,在此分享一下費用流做法:
因爲我們每次需要優先滿足排名在前的選手,志願在前的導師,所以說我們S向每個選手連費用爲1000*排名,流量爲1的邊,每個導師向T連費用爲0,流量爲人數上限的邊,每個選手嚮導師連費用爲志願優先級,流量爲1的邊,這樣對於每次跑出的費用V, 是這個選手的排名, 是這個選手能滿足的最高志願。
但是這樣直接跑是錯的(過不了大樣例),我們需要每次重新建圖,對於每個已知結果的選手,我們連邊的時候只能連和結果志願一樣的邊,理由大概就是後面的流在跑的時候可能會通過前面建的迴流來影響前面的結果
這樣我們需要枚舉每個選手,然後每次重新建圖+費用流
至於第二問只需要二分一下就好了
複雜度大概是 (費用流複雜度大概是 )
代碼(TLE):
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=410;
const int inf=1e9;
int n,m,t,C,S,T,sz,head[N],ans[N],a[300][300],b[300],dis[N],q[N*N],s[N];
bool vis[N];
int pre[N],prv[N];
struct edg{
int to,next,v,f;
}e[200*N];
void add(int x,int y,int f,int v)
{
sz++;e[sz].to=y;e[sz].f=f;e[sz].v=v;e[sz].next=head[x];head[x]=sz;
sz++;e[sz].to=x;e[sz].f=0;e[sz].v=-v;e[sz].next=head[y];head[y]=sz;
}
bool SPFA()
{
int tt=0,hh=0;
for (int i=S;i<=T;++i) vis[i]=0,dis[i]=inf;
vis[S]=1;dis[S]=0;q[++tt]=S;
while (hh<tt)
{
int x=q[++hh];vis[x]=0;
for (int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if (e[i].f&&dis[y]>dis[x]+e[i].v)
{
dis[y]=dis[x]+e[i].v;
pre[y]=i;prv[y]=x;
if (!vis[y]) q[++tt]=y,vis[y]=1;
}
}
}
return (dis[T]!=inf);
}
int main()
{
freopen("mentor.in","r",stdin);
freopen("mentor.out","w",stdout);
scanf("%d%d",&t,&C);
while (t--)
{
sz=1;memset(head,0,sizeof(head));
scanf("%d%d",&n,&m);S=0;T=n+m+1;
for (int i=1;i<=n;++i) ans[i]=m+1;
for (int i=1;i<=m;++i) scanf("%d",&b[i]);
for (int i=1;i<=n;++i)
{
sz=1;memset(head,0,sizeof(head));
for (int j=1;j<=n;++j) add(S,j,1,j*1000);
for (int j=1;j<=m;++j) add(n+j,T,b[j],0);
for (int j=1;j<i;++j)
for (int k=1;k<=m;++k)
if (a[j][k]==ans[j]) add(j,n+k,1,a[j][k]);
for (int j=1;j<=m;++j)
{
scanf("%d",&a[i][j]);
if (a[i][j]) add(i,n+j,1,a[i][j]);
}
while (SPFA())
{
ans[dis[T]/1000]=dis[T]%1000;
for (int i=T;i!=S;i=prv[i])
e[pre[i]].f--,e[pre[i]^1].f++;
}
}
for (int x,i=1;i<=n;++i)
{
scanf("%d",&x);s[i]=0;
if (x>=ans[i]) continue;
int l=1,r=i-1;s[i]=i;
while (l<=r)
{
sz=1;memset(head,0,sizeof(head));
for (int j=1;j<=n;++j) add(S,j,1,j*1000);
for (int j=1;j<=m;++j) add(n+j,T,b[j],0);
int mid=l+r>>1;
for (int j=1;j<mid;++j)
for (int k=1;k<=m;++k)
if (a[j][k]==ans[j]) add(j,n+k,1,a[j][k]);
for (int j=1;j<=m;++j)
if (a[i][j]) add(i,n+j,1,a[i][j]);
bool p=0;
while (SPFA())
{
if (dis[T]/1000==i)
{
p=1;
if (dis[T]%1000<=x) {s[i]=i-mid;l=mid+1;break;}
else {r=mid-1;break;}
}
//cout<<dis[T]<<endl;
for (int i=T;i!=S;i=prv[i])
e[pre[i]].f--,e[pre[i]^1].f++;
}
if (!p) r=mid-1;
}
}
for (int i=1;i<=n;++i)
printf("%d ",ans[i]);
printf("\n");
for (int i=1;i<=n;++i)
printf("%d ",s[i]);
printf("\n");
}
}
普通網絡流思路和費用流差不多,只需要對於每個選手枚舉一個志願即可了,對於每個選手只需要重新建邊一次,枚舉志願時可以直接加邊
第二問也是二分
複雜度 ( 是二分圖dinic複雜度)
代碼:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=410;
const int inf=1e9;
int n,m,t,C,S,T,sz,head[N],v[N],ans[N],a[300][300],b[300],dis[N],q[N],s[N];
int dep[N];
int pre[N],prv[N];
struct edg{
int to,next,f;
}e[200*N];
void add(int x,int y,int f)
{
sz++;e[sz].to=y;e[sz].f=f;e[sz].next=head[x];head[x]=sz;
sz++;e[sz].to=x;e[sz].f=0;e[sz].next=head[y];head[y]=sz;
}
bool bfs()
{
int hh=0,tt=0;
for (int i=S;i<=T;i++) dep[i]=0;
dep[S]=1;q[++tt]=S;
while (hh<tt)
{
int x=q[++hh];
for (int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if (e[i].f&&!dep[y]) dep[y]=dep[x]+1,q[++tt]=y;
}
}
return (dep[T]!=0);
}
int dfs(int x,int flow)
{
if (x==T) return flow;
int used=0,now=0;
for (int i=v[x];i;i=e[i].next)
{
int y=e[i].to;
if (dep[y]==dep[x]+1&&e[i].f)
{
now=dfs(y,min(flow-used,e[i].f));
e[i].f-=now;
e[i^1].f+=now;
used+=now;
v[x]=i;
if (flow==used) break;
}
}
if (used==0) dep[x]=-1;
return used;
}
int main()
{
// freopen("mentor.in","r",stdin);
// freopen("mentor.out","w",stdout);
scanf("%d%d",&t,&C);
while (t--)
{
sz=1;memset(head,0,sizeof(head));
scanf("%d%d",&n,&m);S=0;T=n+m+1;
for (int i=1;i<=n;++i) ans[i]=m+1;
for (int i=1;i<=m;++i) scanf("%d",&b[i]);
int tag=0;
for (int i=1;i<=n;++i)
{
sz=1;memset(head,0,sizeof(head));
for (int j=1;j<=i;++j) {if (j==i) tag=sz+1;add(S,j,1);}
for (int j=1;j<=m;++j) add(n+j,T,b[j]);
for (int j=1;j<i;++j)
for (int k=1;k<=m;++k)
if (a[j][k]==ans[j]) add(j,n+k,1);
for (int j=1;j<=m;++j)
scanf("%d",&a[i][j]);
while (bfs())
{
for (int j=S;j<=T;++j) v[j]=head[j];
dfs(S,inf);
}
for (int l=1;l<=m;++l)
{
for (int j=1;j<=m;++j)
if (a[i][j]==l) add(i,n+j,1);
while (bfs())
{
for (int j=S;j<=T;++j) v[j]=head[j];
dfs(S,inf);
}
if (e[tag].f==0) {ans[i]=l;break;}
}
}
for (int x,i=1;i<=n;++i)
{
scanf("%d",&x);s[i]=0;
if (x>=ans[i]) continue;
int l=1,r=i-1;s[i]=i;
while (l<=r)
{
sz=1;memset(head,0,sizeof(head));
for (int j=1;j<=n;++j) {if (j==i) tag=sz+1;add(S,j,1);}
for (int j=1;j<=m;++j) add(n+j,T,b[j]);
int mid=l+r>>1;
for (int j=1;j<mid;++j)
for (int k=1;k<=m;++k)
if (a[j][k]==ans[j]) add(j,n+k,1);
while (bfs())
{
for (int j=S;j<=T;++j) v[j]=head[j];
dfs(S,inf);
}
for (int j=1;j<=m;++j)
if (a[i][j]>0&&a[i][j]<=x) add(i,n+j,1);
while (bfs())
{
for (int j=S;j<=T;++j) v[j]=head[j];
dfs(S,inf);
}
if (e[tag].f==0) {l=mid+1,s[i]=i-mid;}
else r=mid-1;
}
}
for (int i=1;i<=n;++i)
printf("%d ",ans[i]);
printf("\n");
for (int i=1;i<=n;++i)
printf("%d ",s[i]);
printf("\n");
}
}