【BZOJ3291】Alice与能源计划(贪心)(匈牙利算法)(模拟费用流)

传送门


题解:

首先把所有已经修建好了的塔的代价加到答案里面,然后代价取负。

这样就是最小化所有选择了的塔的代价。显然的费用流的模型。

同时显然直接跑费用流会T掉,考虑到我们实际上可以直接贪心如果一个代价小的塔能够选就直接选上。

那么我们不需要退流,需要考虑如何判断这个塔能不能选。

直接匈牙利即可。代价相同的先选小的即可保证字典序最小。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=5e2+7;

int n,m;
int X[N],Y[N],P[N];
int x[N],y[N],p[N],co[N],r[N],id[N];
inline bool cmp_(int i,int j){
	return co[i]==co[j]?i<j:co[i]<co[j];
}
inline bool check(int i,int j){
	return (x[i]-X[j])*(x[i]-X[j])+(y[i]-Y[j])*(y[i]-Y[j])<=r[i]*r[i];
}
int vis[N],match[N];
int el[N],nxt[N*N],to[N*N],ec;
bool find(int u){
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]]){
		if(!vis[v]){
			vis[v]=true;
			if(!match[v]||find(match[v]))
			return match[v]=u,true;
		}
	}
	return false;
}
inline void solve(){
	n=gi(),m=gi();ll ans=0;int ct=0;ec=0;
	for(int re i=1;i<=n;++i)X[i]=gi(),Y[i]=gi(),P[i]=gi();
	for(int re i=1;i<=m;++i){
		x[i]=gi(),y[i]=gi(),p[i]=gi(),co[i]=gi(),r[i]=gi();
		if(gi())ans+=co[i],co[i]=-co[i];id[i]=i;
	}
	std::sort(id+1,id+m+1,cmp_);
	memset(el,0,sizeof(int)*(m+1));
	memset(match,0,sizeof(int)*(n+1));
	for(int re i=1;i<=m;++i){
		int u=id[i];
		for(int re j=1;j<=n;++j)
		if(p[u]>=P[j]&&check(u,j))
		nxt[++ec]=el[u],el[u]=ec,to[ec]=j;
	}
	for(int re i=1;i<=m&&ct<n;++i){
		memset(vis,0,sizeof(int)*(n+1));
		if(find(id[i]))++ct,ans+=co[id[i]];
	}
	if(ct<n)return (void)puts("-1");
	memset(vis,0,sizeof(int)*(m+1));
	for(int re i=1;i<=n;++i)vis[match[i]]=true;
	cout<<ans<<"\n";
	for(int re i=1;i<=m;++i)if(vis[i])
	cout<<i<<" ";cout<<"\n";
}

signed main(){
#ifdef zxyoi
	freopen("tower.in","r",stdin);
#endif
	int T=gi();while(T--)solve();
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值