【Codechef Dec14. Course Selection】

题目描述

课业计划包含N项课程,需要在M个钟的某一个完成(每学期中可以完成若干个)。
K个限制,形如(Ai,Bi),表示Ai需要比Bi先完成(不能在同一个学期)。
每门课在不同的学期会有不同的分数,Xi,j表示第i门课在第j学期学的分数。若Xi,j=1,则说明第j个学期没有第i门课。
求在学完所有课的情况下分数平均值得最大值。
链接在这里

分析

处理这种规划问题一般就是动态规划和网络流了(还有贪心)。这个题目明显不太能动归,没法转移,我们考虑网络流。
很容易会去想费用流的问题(也许只有我),因为如果没有先学后学的问题那么费用流一跑就出来了。但是后来应注意到费用流是比较难以处理冲突的。而最小割则经常可以使用流量无限的边表示冲突。所以我们考虑最小割。
那么应该是某个东西减去最小割为最大分数。所有我们初值应该是某个“总分”,最小割表示扔掉的分数。如果是用一门课所有学期的分数之和为初值减去最小割,这个看着就不太对。所以总分应该是每门学科的最大分值,换成别的则表示扣分。为了让每个学科的扣分只需选择一个,每门学科自己应当是一条链(串联);不同学科的扣分累加,那么不同学科应该是若干条链(并联)。
为了方便,可以对每个学科不统计其最大得分,而是用一个极大值作为初始值。本题中分数均小于100,可以以100为每门学科的初值。
于是初步的建图如下:
对于每个学科i建立m个点,S(i,1)INF, (i,j)(i,j+1)100Xi,j(i,m)T),如果Xi,j=1,则把100Xi,j改为INF。这样割边就是扣掉那节课对应的分数,表示选择了在那时上那门课。
现在考虑限制。做过【HNOI2013】切糕的同学可以进行类比。AiBi早选,实际上便是Ai对应的割边在Bi之前,那么考虑让Bi的割边在Ai之前的方案不合法,也即仍然连通。那么我们把(Ai,j)(Bi,j+1)连一条INF,这样就使得所有Bi的割边小于等于Ai割边的方案会流一条INF边,表达了限制。
于是我们解决了本题。

代码

#include <bits/stdc++.h>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#define MAXN 100000
#define MOD 
#define INF (1<<20)
#define LLINF (1LL<<50)
#define ri register int
#define il inline
#define fir first
#define sec second
#define pb push_back
using namespace std;
typedef long long LL;
typedef long double LD;
int n, m, k, cnt, s, t, q[MAXN<<2], level[MAXN<<2];
struct node {
    int v; LL c;
    node *next, *rev;
}pool[MAXN<<3], *h[MAXN<<2], *cur[MAXN<<2];
il void adde(int u, int v, LL c) {
    node *p = &pool[cnt++], *q = &pool[cnt++];
    *p = node {v, c, h[u], q}, h[u] = p;
    *q = node {u, 0, h[v], p}, h[v] = q;
}
il int Hash(int x, int y) {
    return (x-1)*m+y;
}
bool bfs() {
    int front = 0, rear = 1;
    memset(level, 0, sizeof(level));
    q[rear++] = s;
    level[s] = 1;
    while(front < rear) {
        int u = q[front++];
        for(node *p = h[u]; p; p = p->next) {
            if(p->c && !level[p->v]) {
                level[p->v] = level[u]+1, q[rear++] = p->v;
                if(p->v == t) return 1;
            }
        }
    }
    return 0;
}
LL dfs(int u, LL f) {
    if(u == t) return f;
    LL flow = 0, F;
    for(node *&p = cur[u]; p; p = p->next) {
        if(p->c && level[p->v] == level[u]+1) {
            F = dfs(p->v, min(f-flow, p->c));
            flow += F, p->c -= F, p->rev->c += F;
            if(flow == f) return flow; 
        }
    }
    return flow;
}
LL Dinic() {
    int ret = 0;
    while(bfs()) {
        for(ri i = s; i <= t; ++i) cur[i] = h[i];
        ret += dfs(s, INF);
    }
    return ret;
}
int main()
{
    int w, u, v, fw;
    scanf("%d%d%d", &n, &m, &k);
    t = n*m+1;
    for(ri i = 1; i <= n; ++i) {
        adde(s, Hash(i, 1), INF);
        for(ri j = 1; j < m; ++j) {
            scanf("%d", &w);
            if(w == -1) fw = INF; else fw = 100-w;
            adde(Hash(i, j), Hash(i, j+1), fw);
        }
        scanf("%d", &w);
        if(w == -1) fw = INF; else fw = 100-w;
        adde(Hash(i, m), t, fw);
    }
    while(k--) {
        scanf("%d%d", &u, &v);
        for(ri i = 1; i < m; ++i) adde(Hash(u, i), Hash(v, i+1), INF);
        adde(Hash(u, m), t, INF);
    }
    int sum = 100*n-Dinic();
    printf("%.2lf", 1.0*sum/n);
    return 0;
}
posted @ 2018-09-02 14:49  hychyc1  阅读(276)  评论(0编辑  收藏  举报