传送门:【HDU】4406 GPA
题目分析:今天郁闷死啊。。。M取小了竟然一直没查出来T ^ T。。。而且数据真恶心,用的还是四舍五入(我linux下是五凑偶的),让我半天纠结了精度导致没发现M的问题。。
输入得到所有课程的信任度wi[ i ]。sum = sum{ 1600 * wi[ i ] }。
建边形如( 弧头 , 弧尾 , 容量 , 费用 )。
由于随着大于等于60分以后每多得到一分,GPA就会增大,所以我们就需要得到的费用最大,将费用取反就是使得到的费用最小。接下来的建边费用都是取反了的。
首先所有的科目如果有小于60分的,建边( i , t , 60 - score , -M )(M要比能得到的最大分数还大,取1e7就好了),表示这部分的分必须先拿到,need += 60 - score(need表示将让所有科目不挂科至少需要的分数)。然后建边( i , t , 1 , wi[ i ] * 3 * ( 2 * j + 1 - 2 * 40 ) ),0 <= j < 40 。
否则如果有科目i的分数score大于等于60分的,ans += wi[ i ] * ( 4 * 1600 - 3 * ( 100 - score ) * ( 100 - score ) ),同时建边( i , t , 1 , wi[ i ] * 3 * ( 2 * j + 1 - 2 * ( 100 - score ) ),score - 60 <= j < 40。
最后对所有的天i建边( s , i + m , K , 0 ) , 所有天向该天能复习的科目建边( i + m , j , K , 0 )。
跑一遍最小费用最大流,如果(-cost)/M不等于need则无解输出0.000000,否则输出ans/sum/1600。
代码如下:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define REP( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i )
#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define REV( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; -- i )
#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define CLR( a , x ) memset ( a , x , sizeof a )
#define cPY( a , x ) memcpy ( a , x , sizeof a )
typedef long long LL ;
const int M = 1e7 ;//3 * 1600 * 40 * 20 = 3840000 < 1e7
const int MAXN = 65 ;
const int MAXE = 10000 ;
const LL INF = 1e15 ;
struct Edge {
int v , c , w , n ;
Edge () {}
Edge ( int v , int c , int w , int n ) : v ( v ) , c ( c ) , w ( w ) , n ( n ) {}
} ;
struct Net {
Edge E[MAXE] ;
int H[MAXN] , cntE ;
int cap[MAXN] , cur[MAXN] ;
LL d[MAXN] ;
int Q[MAXN] , head , tail ;
bool vis[MAXN] ;
int s , t ;
int flow ;
LL cost ;
int n , m , K ;
int wi[MAXN] ;
void init () {
cntE = 0 ;
CLR ( H , -1 ) ;
}
void addedge ( int u , int v , int c , int w ) {
E[cntE] = Edge ( v , c , w , H[u] ) ;
H[u] = cntE ++ ;
E[cntE] = Edge ( u , 0 , -w , H[v] ) ;
H[v] = cntE ++ ;
}
bool spfa () {
REP ( i , 0 , MAXN ) d[i] = INF ;
CLR ( vis , 0 ) ;
head = tail = 0 ;
cap[s] = M ;
cur[s] = -1 ;
d[s] = 0 ;
Q[tail ++] = s ;
while ( head != tail ) {
int u = Q[head ++] ;
if ( head == MAXN ) head = 0 ;
vis[u] = 0 ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v , c = E[i].c , w = E[i].w ;
if ( c && d[v] > d[u] + w ) {
d[v] = d[u] + w ;
cap[v] = min ( c , cap[u] ) ;
cur[v] = i ;
if ( !vis[v] ) {
vis[v] = 1 ;
//if ( d[v] < d[Q[head]] ) {
// if ( head == 0 ) head = MAXN ;
// Q[-- head] = v ;
//} else {
Q[tail ++] = v ;
if ( tail == MAXN ) tail = 0 ;
//}
}
}
}
}
if ( d[t] == INF ) return 0 ;
flow += cap[t] ;
cost += cap[t] * d[t] ;
for ( int i = cur[t] ; ~i ; i = cur[E[i ^ 1].v] ) {
E[i].c -= cap[t] ;
E[i ^ 1].c += cap[t] ;
}
return 1 ;
}
int MCMF () {
flow = cost = 0 ;
while ( spfa () ) ;
return cost ;
}
void scanf ( int& x , char c = 0 ) {
while ( ( c = getchar () ) < '0' || c > '9' ) ;
x = c - '0' ;
while ( ( c = getchar () ) >= '0' && c <= '9' ) x = x * 10 + c - '0' ;
}
void solve () {
int c , score ;
int ans = 0 , sum = 0 ;
int need = 0 ;
init () ;
s = 0 ;
t = n + m + 1 ;
FOR ( i , 1 , m ) {
scanf ( wi[i] ) ;
sum += 1600 * wi[i] ;
}
FOR ( i , 1 , m ) {
scanf ( score ) ;
if ( score < 60 ) {
addedge ( i , t , 60 - score , -M ) ;//M一开始只取了1e6,wa到死啊。。。
need += 60 - score ;
score = 60 ;
}
ans += wi[i] * ( 4 * 1600 - 3 * ( 100 - score ) * ( 100 - score ) ) ;
int x = 100 - score ;
REP ( j , 0 , x ) addedge ( i , t , 1 , wi[i] * 3 * ( j * 2 + 1 - x * 2 ) ) ;
}
FOR ( i , 1 , n ) {
addedge ( s , m + i , K , 0 ) ;
FOR ( j , 1 , m ) {
scanf ( c ) ;
if ( c ) addedge ( m + i , j , K , 0 ) ;
}
}
MCMF () ;
if ( ( -cost ) / M != need ) printf ( "%.6f\n" , 0.0 ) ;
else {
ans += ( -cost ) % M ;
printf ( "%.6f\n" , ( double ) ans / sum ) ;
}
}
} e ;
int main () {
while ( ~scanf ( "%d%d%d" , &e.n , &e.K , &e.m ) && ( e.n || e.K || e.m ) ) e.solve () ;
return 0 ;
}