[P1149] TSOI南湖探险(费用流)

背景

NOIP2009,TSOI大爆发,创造了历史,书写了传奇。于是,在SRC的提议下,大家一起去南湖公园探险。
描述

南湖地区的地形错综复杂。这里有茂密的原始森林,有一望无际的湖水,有大自然一切钟灵毓秀的景物。孩子们一旦走散,彼此就再也看不见了,除非彼此选择的道路在前方的某个地方汇合。
在TSOI光环的感召下,大家的审美和价值观趋于一致,对于相同景物感到的愉悦程度可以看做一样的。这种愉悦程度可以在斐波那契奥斯特洛夫斯基参考系(Universal Fibonacci Ostrovsky参考系,以下简称UFO系)下用一个不大于500000的正整数表示出来。而景物都是在道路的交点处出现的。这些道路都是单向的,而且路网中不存在圈。我们从起点开始,无论选择哪条路,都要一直走下去,并且一定能走到终点。
这时,喜欢思考的芥末和擅长分析的大隋与低调的板砖和强悍的Kib找到了实力派的畅牛和博学的DMK,商量着要刁难一下经常打Dota却在NOIP2009中拔得河北省头筹的Zjoe。他们的问题是这样的,假设TSOI小组共有K个人,那么他们在探险中能欣赏到的景物集合的并所包含的愉悦程度的和最大是多少;如果我们想要游览所有的道路交点,那TSOI小组至少要有多少人。
Zjoe很纠结地囧在那里,等待你的帮助。

输入格式

第一行是三个数N、M、K,N表示道路交点个数,M表示道路条数,K表示TSOI小组人数。起始点为1号点,终止点为N号点。
第2到M+1行每行为两个整数U、V,表述U到V号交点之间有一条单向道路。
第M+2到M+N+1行每行一个非负整数P,表示该交点有一个愉悦程度为P的景物。如果该点没有景物,则P为0。
数据保证起点和终点没有景物。

输出格式

两个数,中间用空格隔开。第一个数是K个人在探险中能欣赏到的景物集合的并包含的愉悦程度的和最大值,第二个数是游览所有的交点需要的最少人数。

测试样例1

输入

7 8 2
1 2
1 4
2 3
4 5
4 6
5 3
6 7
3 7
0
2
3
4
5
6
0
输出

18 3
备注

N≤100,M≤2000,K≤100
P≤500000由SRC原创


旧版题解:
第一问是费用流,把1、n分别拆成两个点,中间连容量是人数,费用是0的边,把其他每个点p拆成p1,p2,p1到p2有两条边,一条费用是点的权值,容量1,另一条费用是0,容量无穷大。如果原来点q到点p有一条边,则q2到p1连一条费用0,容量无穷大的边。
第二问可以用最小路径覆盖解决,不过之前要做的处理是,如果p能走到q(无论是直接还是间接),就连一条p到q的边,这个预处理可以用floyd解决,路径覆盖就用匈牙利解决,于是出解
这题数据有问题

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>

using namespace std;

typedef long long LL;

void read(int &x) {
    char c;bool flag = 0;
    while((c=getchar())<'0'||c>'9') flag |= (c=='-');
    x=c-'0';while((c=getchar())>='0'&&c<='9') x = (x<<3)+(x<<1)+c-'0';
    flag?x=-x:x;
}

#define MAXX 500000
#define MAXN 11000
const int inf = ~0u>>2;
typedef int T;

struct E {
	int to,flow,next;T cost;
	E(int to=0,int flow=0,T cost=0,int next=0):to(to),flow(flow),cost(cost),next(next){}
}g[MAXX];
int tot = 1,fr[MAXN];

void Add(int from,int to,int flow,T cost=0) {
//cout<<"E:"<<from<<" "<<to<<" "<<flow<<" "<<cost<<"\n";
	g[++tot] = E(to,flow,cost,fr[from]);
	fr[from] = tot;
	g[++tot] = E(from,0,-cost,fr[to]);
	fr[to] = tot;
}

int n,k,m;
LL _cost,_flow;
int d[MAXN];
int st,ed,pere[MAXN],inq[MAXN],fl[MAXN];

bool spfa() {
	static deque<int> q;
	for (int i = 0; i < ed+10; i++) {
		inq[i] =  fl[i] = 0;
		d[i] = -inf;
	}
	inq[st] = 1; d[st] = 0; q.push_back(st); fl[st] = inf;
	while(q.size()) {
		int t = q.front(); q.pop_front(); inq[t] = 0;
		for (int i = fr[t]; i; i = g[i].next) {
			int to = g[i].to;
			if((d[to] < d[t]+g[i].cost) && g[i].flow) {
				pere[to] = i;
				d[to] = d[t]+g[i].cost;
				fl[to] = min(fl[t],g[i].flow);
				if(!inq[to]) {
				  if(q.size() && d[q.front()] > d[to]) q.push_front(to);
				  else q.push_back(to);
				}
				inq[to] = 1;
			}
		}
	}
	return d[ed] != -inf;
}

void mcmf() {
	_cost += d[ed]*fl[ed];
	_flow += fl[ed];
	for (int t = ed,e = pere[t] ; t != st; ) {
		g[e].flow -= fl[ed];
		g[e^1].flow += fl[ed];
		t = g[e^1].to;
		e = pere[t];
 	}
}

struct WWW{int x,y;}ss[3000];

namespace WOW {
	int d[MAXN],st,ed,sa,cur[MAXN];

    bool bfs() {
       memset(d,-1,sizeof(d[0])*(ed+10));
	   queue<int> q;
	   q.push(st); d[st] = 0;
	   while(q.size()) {
	  	  int t = q.front(); q.pop();
		  for (int i = fr[t]; i; i = g[i].next) {
			int to = g[i].to;
			if(d[to] == -1 && g[i].flow) {
				d[to] = d[t]+1;
				q.push(to);
			}
		  }  
	   }
	  return d[ed] != -1;
   }

   int dfs(int t,int mf) {
	  if(t == ed || mf == 0) return mf;
	  int tmp = 0;
	  for (int i = fr[t]; i&&mf; i = g[i].next) {
		if(g[i].flow == 0 || d[g[i].to] != d[t]+1) continue;
		int f = dfs(g[i].to,min(mf,g[i].flow));
		mf -= f; tmp += f;
		g[i].flow -= f; g[i^1].flow += f;
	  }
	  if(!tmp) d[t] = -1;
	  return tmp;
   }

   int Dinic(int s,int t) {
	  st = s; ed = t;int max_flow = 0;
	  while(bfs()) max_flow += dfs(st,inf);
	  return max_flow;
   }
	 
  void work() {
	  static bool f[101][101];
	  memset(f,0,sizeof f);
	  memset(fr,0,sizeof fr); tot = 1;
	  for (int i = 1; i <= m; i++) {
		 // f[ss[i].x][ss[i].y] = 1;
		 Add(ss[i].x,n+ss[i].y,1);  
	  }
/*	  for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
		  for (int j = 1; j <= n; j++)
			if(k!=i && i!=j && j!=k)
			  f[i][j] |= (f[i][k]&&f[k][j]);
	  for (int i = 1; i <= n; i++) 
	   for (int j = 1; j <= n; j++)
	      if(i!=j && f[i][j]) Add(i,n+j,1); */
	  for (int i = 1; i <= n; i++) {
	      Add(0,i,1);
	      Add(n+i,2*n+1,1);
	  }
	  printf("%d",n-Dinic(0,n+n+1));
	}
}

int main() {
	read(n);read(m);read(k);
	st = 0; ed = n*2; Add(st,1,k,0);
	for (int i = 1; i <= n; i++) Add(i,n+i,1,0),Add(i,n+i,inf,0);
	for (int i = 1; i <= m; i++) read(ss[i].x),read(ss[i].y); 
	for (int i = 1,p; i <= n; i++) {
		read(p);
	    Add(i,n+i,1,p);
		Add(i,n+i,inf,0);
	}
	for (int i = 1; i <= m; i++) Add(n+ss[i].x,ss[i].y,inf,0);
	while(spfa()) mcmf();
    printf("%d ",_cost);
   // cerr<<"-------------------\n";
    WOW::work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值