1. 程式人生 > >CodeChef DEC14 Course Selection 最小割,建圖,割邊

CodeChef DEC14 Course Selection 最小割,建圖,割邊

題意: n門課程,m個學期. 同一門課可能在多個學期中都有開設

a[i][j]表示在第j個學期修第i門課的成績.  a[i][j]=-1表示第j個學期沒有開設第i門課.

k個修課條件(u,v) 表示要修第v門課 必須先修完第u門課.

n,m,k,a[i][j]<=100. 問修完n門課的最多平均分為多少?

 

分母n為固定的,總得分越大,平均分也越大. 反著考慮,總的扣分儘量小(從最小割角度考慮),令a[i][j]= 100 - a[i][j].

建圖:s向(i,1)連線容量為a[i][1]的邊, 對j=[1:m-1] :(i,j)向(i,j+1)連線一條容量為 a[i][j]的邊. (i,m)向匯點連線容量為inf的邊.

此時的最小割就是不考慮k個條件時的最小總扣分 (s->i->t 的路徑上正好一條邊被割去,否則存在路徑).

 

若有修課條件(u,v) 意味著u的割邊必須要在v的割邊之前.

s向(v,1)連線容量為inf的邊.(u,j)向(v,j+1)連線一條容量為inf的邊.

這樣建圖u的割邊一定在v的割邊之前.【若u割邊在v之後,則s->u->v->t存在路徑】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=2e2+5,M=6e4+5,inf=0x3f3f3f3f;
int n,m,k,a[N][N],head[N*N],tot,s,t,dis[N*N];
void init(){
	tot=0;
	memset(head,-1,sizeof(head));
	s=0,t=n*m+1;
}
struct edge{
	int to,nxt,cap;
	edge(){}
	edge(int to,int nxt,int cap):to(to),nxt(nxt),cap(cap){}
}e[M];
void add_edge(int u,int v,int cap){
	e[tot]=edge(v,head[u],cap);
	head[u]=tot++;
	e[tot]=edge(u,head[v],0);
	head[v]=tot++;
}
bool bfs(){
	queue<int> q;
	memset(dis,-1,sizeof(dis));
	q.push(s),dis[s]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=e[i].nxt){
			int v=e[i].to;
			if(dis[v]==-1&&e[i].cap>0)	dis[v]=dis[u]+1,q.push(v);
		}
	}
	return dis[t]!=-1;
}
int dfs(int u,int x){
	if(u==t||x==0)	return x;
	int res=0;
	for(int i=head[u];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if(dis[v]!=dis[u]+1)	continue;
		int dx=dfs(v,min(e[i].cap,x));
		if(dx>0){
			e[i].cap-=dx,e[i^1].cap+=dx;
			x-=dx,res+=dx;
			if(x==0)	return res;
		}
	}
	dis[u]=-1;
	return res;
}
int Dinic(){
	int res=0;
	while(bfs())	res+=dfs(s,inf);
	return res;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin>>n>>m>>k;
	init();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){	
			cin>>a[i][j];
			if(a[i][j]==-1)	a[i][j]=inf;
			else a[i][j]=100-a[i][j];
			if(j==1)	add_edge(s,(i-1)*m+j,a[i][j]);
			else add_edge((i-1)*m+j-1,(i-1)*m+j,a[i][j]);
		}
		add_edge((i-1)*m+m,t,inf);
	}
	for(int p=0;p<k;p++){
		int u,v;
		cin>>u>>v;
		for(int j=1;j<=m;j++){
			if(j==1)	add_edge(s,(v-1)*m+j,inf);
			else add_edge((u-1)*m+j-1,(v-1)*m+j,inf);
		}
	}
	double res=100*n-Dinic();
	cout<<fixed<<setprecision(2)<<res/(1.0*n)<<'\n';
	return 0;
}