洛谷4382[八省联考2018]劈配

省选滚粗回家。
满怀信心的day2t1网络流。一个半小时自信过样例。然后就回家了。

首先能很快的看出这是一个网络流。
第一问:我们考虑匹配,按顺序枚举人,再按顺序枚举优先级,每次在前一个优先级的残余网络上加边,跑最大流,若流量是1,则说明这是他的理论可能的最高优先级,去做下一个人,否则继续枚举优先级。
第一问较简单。。考场上写的程序所有点的第一问都能过,然后第二问炸了。
第二问:二分他的排名,去重新建图检验即可。。。
考场上不知道怎么算的二分的复杂度会爆炸,对于每次二分后要重新建图,建图时要枚举人和优先级是n²,dinic是nm,结果就是n^4,算了算会爆炸。。。于是我就写了一个贪心。。找到他所有“要求的最低优先级的那些导师”的选择者,找到排名最低的那个人让他的排名高即可。。。场上我也不知道我是怎么觉得他是对的。一出来讨论一下就发现崩了:把排名上调的太小有可能因为前面的人加边少了而无法调剂,就无法达到我们的目的。

然而贪心竟然0分。。。原来是数组没清空。。。心态爆炸。

另外!每次建图的时候要把前面的清空重新对于已经确定的优先级重新建图,保证了边数较小,否则会T,60分。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cstdlib>
#include<ctime>
#include<cctype>
#define ll long long
#define ull unsigned long long
#define inf 0x3f
#define INF 0x3f3f3f3f

using namespace std;

inline ll read()
{
    ll f=1,num=0;char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
    while (isdigit(c)) {num=(num<<1)+(num<<3)+c-'0';c=getchar();}
    return f*num;
}
const int MAXN=410;
const int MAXM=200*MAXN;
struct edge
{
    int next,to,val;
};
edge e[MAXM];
int head[MAXN],cnt=1;
void addedge(int u,int v,int w)
{
    e[++cnt].next=head[u];
    e[cnt].to=v;
    e[cnt].val=w;
    head[u]=cnt;
    swap(u,v);
    e[++cnt].next=head[u];
    e[cnt].to=v;
    e[cnt].val=0;
    head[u]=cnt;
}
int level[MAXN],s,t;
queue <int> q;
bool bfs()
{
    memset(level,-1,sizeof(level));
    q.push(s);
    level[s]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=head[x];i;i=e[i].next)
        {
            int v=e[i].to;
            if (level[v]!=-1 || e[i].val<=0)
                continue;
            level[v]=level[x]+1;
            q.push(v);
        }
    }
    if (level[t]==-1) return false;
    return true;
}
int dfs(int x,int f)
{
    if (x==t) return f;
    int w,tot=0;
    for (int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if (level[v]!=level[x]+1 || e[i].val<=0)
            continue;
        w=dfs(v,min(f-tot,e[i].val));
        e[i].val-=w;
        e[i^1].val+=w;
        tot+=w;
        if (tot==f) return f;
    }
    if (f-tot) level[x]=-1;
    return tot;
}
int max_flow()
{
    int ans=0;
    while (bfs())
        ans+=dfs(s,INF);
    return ans;
}
int a[210][210],ans[210],b[210],wt[210],ss[210],flag[210][210];
void init()
{
    memset(head,0,sizeof(head));
    cnt=1;
}
vector <int> cho[210][210];
void build(int x,int n,int m)
{
    for (int i=1;i<=m;i++)
        addedge(i+n,t,b[i]);
    for (int i=1;i<=n;i++)
        addedge(s,i,1);
    for (int i=1;i<=x;i++)
    {
        if (ans[i]==m+1) continue;
        for (int j=0;j<(int)cho[i][ans[i]].size();j++)
            addedge(i,cho[i][ans[i]][j]+n,1);
    }
}
int main()
{
    freopen("mentor.in","r",stdin);
    freopen("mentor.out","w",stdout);
    int T,C;
    scanf("%d%d",&T,&C);
    while (T--)
    {
        memset(flag,0,sizeof(flag));
        memset(wt,0,sizeof(wt));
        memset(ans,0,sizeof(ans));
        init();
        int n,m;
        scanf("%d%d",&n,&m);
        s=0,t=n+m+1;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
                cho[i][j].clear();
        for (int i=1;i<=m;i++)
            scanf("%d",&b[i]);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
                if (a[i][j])
                    cho[i][a[i][j]].push_back(j);
            }
        for (int i=1;i<=n;i++)
            scanf("%d",&ss[i]);
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=m;j++)
                flag[i][j]=flag[i][j-1]+(int)cho[i][j].size();
            if (!flag[i][ss[i]])
                wt[i]=i;
        }
        for (int i=1;i<=n;i++)
        {
            init();
            build(i-1,n,m);
            max_flow();
            int flow=0,num=0;
            while (!flow)
            {
                num++;
                if (num>m) break;
                for (int j=0;j<(int)cho[i][num].size();j++)
                    addedge(i,cho[i][num][j]+n,1);
                flow=max_flow();
            }
            ans[i]=num;
        }
        for (int ii=1;ii<=n;ii++)
        {
            if (ans[ii]<=ss[ii]) continue;
            if (wt[ii]) continue;
            int l=1,r=ii-1,mid,anss=0;
            while (l<=r)
            {
                mid=(l+r)>>1;
                init();
                build(mid-1,n,m);
                int flow=0,num=0;
                max_flow();
                while (!flow)
                {
                    num++;
                    if (num>m) break;
                    for (int j=0;j<(int)cho[ii][num].size();j++)
                        addedge(ii,cho[ii][num][j]+n,1);
                    flow=max_flow();    
                }
                if (num<=ss[ii])
                    l=mid+1,anss=mid;
                else
                    r=mid-1;
            }
            wt[ii]=ii-anss;
        }
        for (int i=1;i<=n;i++)
            printf("%d ",ans[i]);
        putchar('\n');
        for (int i=1;i<=n;i++)
            printf("%d ",wt[i]);
        putchar('\n');
    }   
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值