[日常训练]选课

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}\)的有向边(\(d\in[0,m)\)),

\(<a_i,d>\)\(<b_i,d+1>\)连一条容量为\(+\infty\)的有向边(\(d\in[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\n",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;
}
posted @ 2017-01-10 13:26  Aireen_Ye  阅读(262)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.