一道网络流练习题

题意

现有一款游戏,你作为玩家,拥有k种物品。开始时,每种物品有1000件。

现在,在你面前有n个任务,每种任务都可能消耗一些物品,也可能得到一些物品。做第i个任务的物品得失情况用一个包含k个字母的字符串Si表示,其中每个字母都是+,-,/中的一种,第j个字母表示该任务对物品j的数量的影响。+表示做这个任务能得到一个物品j,-表示做这个任务会消耗一个物品j,/表示做这个任务对物品j的数量不产生影响。

但是,做任务是有前提条件的。游戏设计者约定了m个限制关系,每个限制关系是一个有序数对(i, j),表示做任务i前必须先做任务j。

现在,你需要选择性的做一些任务,使得获得的物品最多。

比较两种方案的获得的物品数的方法如下:先比较他们获得第物品1的个数,若相同,再比较物品2,若相同,再比较物品3,以此类推。

数据范围

n 1000 , m 5000 , k 5 , n\le 1000 ,m\le 5000 ,k\le 5,保证限制关系中没有环

解法

考虑一个任务的奖励很像k位数,所以将其转化成10进制数,然后权值有正有负,考虑对于限制关系建图,方式就是如果做任务i之前需要做任务j,那就从i向j连一条边,然后对于这个图求最大权闭合子图.选出的子图就是答案.

由于我一开始对这样做的正确性抱有怀疑,所以现在稍微证明一下:

首先最大权闭合子图是用所有正权值之和减去原图的最小割,然后我们相对于限制反向建边,就是要求如果要选一个点,则它之前的所有点必须要选.这样就满足了限制.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1e18;
const int maxn=5e3+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,k;
ll p[maxn];
char c[maxn][5];
ll val[maxn];
inline int calc(char c){
	if(c=='+')return 1;
	if(c=='-')return -1;
	return 0;
}
struct edge{
	int v,p;
	ll w;
}e[maxn<<1];
int h[maxn],cnt=1;
inline void add(int a,int b,ll c){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	e[cnt].w=c;
	h[a]=cnt;
}
int s,t,ht[maxn];
ll dis[maxn];
bool bfs(){
	queue<int> q;
	while(!q.empty())q.pop();
	q.push(s);
	memset(dis,0,sizeof(dis));
	dis[s]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=e[i].p){
			int v=e[i].v;
			if(e[i].w&&dis[v]==0){
				dis[v]=dis[u]+1;q.push(v);
			}
		}
	}
	return dis[t];
}
ll dfs(int u,ll rest){
	if(rest==0||u==t){return rest;}
	ll tot=0;
	for(int &i=ht[u];i;i=e[i].p){
		int v=e[i].v;
		if(e[i].w&&dis[v]==dis[u]+1){
			ll di=dfs(v,min(rest,e[i].w));
			e[i].w-=di;e[i^1].w+=di;
			rest-=di;tot+=di;
			if(rest==0)break;
		}
	}
	return tot;
}
ll dinic(){
	ll ans=0;
	while(bfs()){
		ll di=0;
		for(int i=1;i<=t;i++)ht[i]=h[i];
		while(di=dfs(s,inf))ans+=di;
	}
	return ans;
}
ll tmp1[5],tmp2[5];
int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	n=read(),m=read(),k=read();s=n+1,t=n+2;
	p[0]=1;
	for(int i=1;i<k;i++)p[i]=p[i-1]*2*n;
	for(int i=1;i<=n;i++){
		scanf("%s",c[i]);
		for(int j=0;j<k;j++){
			val[i]+=p[k-1-j]*calc(c[i][j]);
		}
	}
	for(int i=1;i<=m;i++){
		int a=read(),b=read();
		add(a,b,inf);
		add(b,a,0);
	}
	ll sum=0;
	for(int i=1;i<=n;i++){
		if(val[i]>0){
			sum+=val[i];
			add(s,i,val[i]);add(i,s,0);
		}
		else{
			add(i,t,-val[i]);add(t,i,0);
		}
	}
	ll ans=dinic();
	if(sum<ans){
		for(int i=0;i<k;i++)printf("1000 ");
		return 0;
	}
	else{
		sum-=ans;
		for(int i=0;i<k;i++){
			if(p[1]){
				tmp1[k-1-i]+=sum%p[1];
				sum/=p[1];
			}
			else{
				tmp1[k-1-i]+=sum;
			}
			//printf("%d\n",tmp1[k-1-i]);
			if(tmp1[k-1-i]>n){
				tmp1[k-i-2]++;tmp1[k-1-i]=tmp1[k-1-i]-2*n;
				//printf("%d\n",k-i);
			}
		}
		for(int i=0;i<k;i++){
			printf("%lld ",1000+tmp1[i]);
		}
		return 0;
	}
	return 0;
}
发布了62 篇原创文章 · 获赞 1 · 访问量 979

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103896631