2055: 80人环游世界

2055: 80人环游世界

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 366   Solved: 232
[ Submit][ Status][ Discuss]

Description

    想必大家都看过成龙大哥的《80天环游世界》,里面的紧张刺激的打斗场面一定给你留下了深刻的印象。现在就有这么
    一个80人的团伙,也想来一次环游世界。
    他们打算兵分多路,游遍每一个国家。
    因为他们主要分布在东方,所以他们只朝西方进军。设从东方到西方的每一个国家的编号依次为1...N。假若第i个人的游历路线为P1、P2......Pk(0≤k≤N),则P1<P2<......<Pk。
    众所周知,中国相当美丽,这样在环游世界时就有很多人经过中国。我们用一个正整数Vi来描述一个国家的吸引程度,Vi值越大表示该国家越有吸引力,同时也表示有且仅
有Vi个人会经过那一个国家。
    为了节省时间,他们打算通过坐飞机来完成环游世界的任务。同时为了省钱,他们希望总的机票费最小。
    明天就要出发了,可是有些人临阵脱逃,最终只剩下了M个人去环游世界。他们想知道最少的总费用,你能告诉他们吗?

Input

第一行两个正整数N,M。
第二行有N个不大于M正整数,分别表示V1,V2......VN。
接下来有N-1行。第i行有N-i个整数,该行的第j个数表示从第i个国家到第i+j个国家的机票费(如果该值等于-1则表示这两个国家间没有通航)。

Output

在第一行输出最少的总费用。

Sample Input

6 3
2 1 3 1 2 1
2 6 8 5 0
8 2 4 1
6 1 0
4 -1
4

Sample Output

27

HINT

1<= N < =100 1<= M <= 79

Source

[ Submit][ Status][ Discuss]



转换模型,,等价于求有上下界的最小费用可行流

每个国家拆成两个点Ai,Bi,中间连上下界都为Vi的边

假设一个源点s,城市0,s向0连一条容量为m的边,0向所有城市的Ai连容量为INF的边

所有城市的Bi向汇点t连容量为INF的边,城市间的飞机就有Bi向Aj连边,容量INF,有费用

只要再连(t,s,INF)这条边就能构成循环流了,按照构建循环流的方法,跑一遍最小费用最大流就是答案了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn = 255;
const int maxm = 4E5 + 40;
const int INF = ~0U>>1;

struct E{
	int to,cap,flow,cost; E(){}
	E(int to,int cap,int flow,int cost):
		to(to),cap(cap),flow(flow),cost(cost){}
}edgs[maxm];

int n,m,s,t,S,T,cnt,Cnt,from[maxn],flow[maxn],cost[maxn];
bool vis[maxn];

vector <int> v[maxn];
queue <int> Q;

void Add(int x,int y,int cap,int cost)
{
	v[x].push_back(cnt); edgs[cnt++] = E(y,cap,0,cost);
	v[y].push_back(cnt); edgs[cnt++] = E(x,0,0,-cost);
}

bool SPFA()
{
	for (int i = 0; i <= T; i++) 
		cost[i] = INF,vis[i] = 0;
	vis[S] = 1; Q.push(S);
	flow[S] = INF; cost[S] = 0;
	while (!Q.empty())
	{
		int k = Q.front(); Q.pop(); vis[k] = 0;
		for (int i = 0; i < v[k].size(); i++)
		{
			E e = edgs[v[k][i]];
			if (e.cap == e.flow) continue;
			if (cost[e.to] > cost[k] + e.cost)
			{
				cost[e.to] = cost[k] + e.cost;
				flow[e.to] = min(flow[k],e.cap - e.flow);
				from[e.to] = v[k][i];
				if (!vis[e.to]) vis[e.to] = 1,Q.push(e.to);
			}
		}
	}
	return cost[T] != INF;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n >> m; s = 2*n + 1; t = s + 1; 
	S = t + 1; T = S + 1; Add(s,0,m,0);
	for (int i = 1; i <= n; i++)
	{
		int x; scanf("%d",&x);
		Add(i,T,x,0); Add(S,i + n,x,0);
		Add(0,i,INF,0); Add(i + n,t,INF,0);
	}
	for (int i = 1; i < n; i++)
		for (int j = i + 1; j <= n; j++)
		{
			int x; scanf("%d",&x);
			if (x == -1) continue;
			Add(i + n,j,INF,x);
		}
	Add(t,s,INF,0); int Ans = 0;
	while (SPFA())
	{
		Ans += flow[T]*cost[T];
		for (int x = T; x != S; x = edgs[from[x]^1].to)
		{
			edgs[from[x]].flow += flow[T];
			edgs[from[x]^1].flow -= flow[T];
		}
	}
	cout << Ans;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值