[日常训练]选课

Description

小$G$非常喜欢学习,热衷于刷$GPA$。新的学期开始,他又要开始挑选课程,以便刷分了。
他的课业计划中共包含$n$项课程,每项课程都需要在$m$周中的某一周完成。课程与周数都从$1$开始编号。
一些课程有前置课程,对于所有的$i(1;leq;i;leq;k)$,$a_i$是$b_i$的前置课程。也就是修完$a_i$才能修$b_i$。
相同的课程在不同周可能会由不同教授授课,不同教授会影响小$G$在这门课上的分数。
我们用一个数组$x_{i,j}$来描述这个信息,对于每项课程$i$和周数$j$,$x_{i,j}$表示在第$j$周修第$i$门课所能得到的分数。如果$x_{i,j}=−1$则说明那周不能修这门课。
一周中可以修多门课,而一门课需要用一整周的时间才算修完。
请帮助小$G$修完所有课程,并使得他得到分数的平均值最大吧。

Input

第一行三个整数$n,m,k$,表示课程数、总周数与前置课程关系数。
接下来$n$行,每行$m$个整数,第$i$行第$j$个数表示$x_{i,j}$。
接下来$k$行,每行包含两个整数$a_i,b_i$。

Output

输出一个实数,表示所能得到的$n$门课程分数平均值的最大值。答案保留两位小数。

Sample Input

4 5 4
20 -1 100 -1 -1
100 30 -1 -1 -1
100 -1 30 20 40
100 30 40 50 20
1 2
1 3
2 4
3 4

Sample Output

32.50

HINT

$1;leq;n,m;leq;100,0;leq;k;leq;100,−1;leq;x_{i,j};leq;100,1;leq;a_i,b_i;leq;n$.

Solution

总数不变,平均值最大即总和最大,考虑用网络流求解.

先将每个课程$i$按完成的的日期$d$拆成点$<i,d>$.

从$s$向$<i,0>$连一条容量为$+infty$的有向边,

从$<i,m>$向$t$连一条容量为$+infty$的有向边,

从$<i,d>$向$<i,d+1>$连一条容量为$100-x_{i,d+1}$的有向边($din[0,m)$),

从$<a_i,d>$向$<b_i,d+1>$连一条容量为$+infty$的有向边($din[0,m)$).

跑最小割$mincut$,答案为$frac{100n-mincut}{n}$.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define K 105
#define N 10105
#define M 40405
#define INF 1000000000
using namespace std;
struct graph{
    int nxt,to,f;
}e[M];
int x[K][K],g[N],dep[N],n,m,k,s,t,cnt;
queue<int> q; 
inline void addedge(int x,int y,int f){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f;
}
inline void adde(int x,int y,int f){
    addedge(x,y,f);addedge(y,x,0);
}
inline bool bfs(int u){
    memset(dep,0,sizeof(dep));
    dep[u]=1;q.push(u);
    while(!q.empty()){
        u=q.front();q.pop();
        for(int i=g[u];i;i=e[i].nxt)
            if(e[i].f>0&&!dep[e[i].to]){
                q.push(e[i].to);
                dep[e[i].to]=dep[u]+1;
            }
    }
    return dep[t];
}
inline int dfs(int u,int f){
    int ret=0;
    if(u==t) return f;
    for(int i=g[u],d;i&&f;i=e[i].nxt)
        if(e[i].f>0&&dep[e[i].to]>dep[u]){
            d=dfs(e[i].to,min(f,e[i].f));
            ret+=d;f-=d;e[i].f-=d;e[i^1].f+=d;
        }
    if(!ret) dep[u]=-1;
    return ret;
}
inline int dinic(){
    int ret=0;
    while(true){
        if(!bfs(s)) return ret;
        ret+=dfs(s,INF);
    }
}
inline void Aireen(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i)
        for(int j=0;j<=m;++j)
            x[i][j]=++cnt;
    s=++cnt;t=++cnt;
    cnt=1;
    for(int i=1;i<=n;++i){
        adde(s,x[i][0],INF);
        adde(x[i][m],t,INF);
    }
    for(int i=1,y;i<=n;++i)
        for(int j=0;j<m;++j){
            scanf("%d",&y);
            if(y<0) adde(x[i][j],x[i][j+1],INF);
            else adde(x[i][j],x[i][j+1],100-y);
        }
    for(int i=1,a,b;i<=k;++i){
        scanf("%d%d",&a,&b);
        for(int j=0;j<m;++j)
            adde(x[a][j],x[b][j+1],INF);
    }
    printf("%.2lf
",100.0-(double)(dinic())/(double)(n));
}
int main(){
    freopen("select.in","r",stdin);
    freopen("select.out","w",stdout);
    Aireen();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
原文地址:https://www.cnblogs.com/AireenYe/p/6269026.html