BZOJ 3291 Alice与能源计划(二分图匹配&贪心)

8 篇文章 0 订阅

BZOJ 3291 Alice与能源计划(二分图匹配&贪心)

题目大意

给出n个村庄和m个能源站,每个村庄有一定的能源需求量和一个坐标,每个能源站有一个坐标、产能上限,价值,作用半径。现在每个能源站最多为一个村庄供能,且要求能源站的产能上限要大于村庄的能量需求量,且两者之间的距离要小于能源站的作用半径。现有一些供能站已经建造完成了,定义总的费用为选用的未建造电站的价格加上已建造的未选用的电站的价值。现在要求选出一些电站,使得总的费用最小,当有多个答案有相同的选择字典序最小的解

解题思路

据题目定义,总的价值就是所有的已经建造的电站的价格减去选用的已经建造电站的价格加上未建造的电站的价值。因此将已经建造的电站的价值定义为其原本价值的负值。贪心地从价值小的电站开始匹配,如果价值相同则优先匹配序号较小的点。在匹配中如非只有通过拆掉已经形成的匹配才能形成更多的匹配则已经建立的匹配都会被保留,由此就可以得到最优解.需要注意的是,最后选出所有的要选的电站之后,需要根据电站的序号排序。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn=405;
const int maxm=505;
double eps=1e-8;
pii locp[maxn];
int poe[maxm];
vector<int> G[maxn+maxm];
struct fac{
	int id;
	int x;int y;
	int lim,pri,r;
	int sta;
	friend bool operator<(fac a,fac b)
	{
		if(a.pri!=b.pri)return a.pri<b.pri;
		return a.id<b.id;
	}
}f[maxm];
int dis(pii a,int xb,int yb)
{
	int xa=a.first,ya=a.second;
	return ((xa-xb)*(xa-xb)+(ya-yb)*(ya-yb));
}
int linker[maxn+maxm],vis[maxn+maxm];
bool dfs(int u)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(!vis[v])
		{
			vis[v]=1;
			if(linker[v]==-1||dfs(linker[v]))
			{
				linker[v]=u;
				linker[u]=v;
				return true;
			}
		}
	}
	return false;
}
int hunger(int n,int m)
{
	memset(linker,-1,sizeof(linker));
	int ans=0;
	for(int i=n+1;i<=n+m&&ans<n;i++)
	{
		memset(vis,0,sizeof(vis));
		if(dfs(i)) ans++;
	}
	return ans;
}
bool can[maxm];
int main()
{
	int t;
	//freopen("in.in","r",stdin);
	//freopen("out.out","w",stdout);
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n+m;i++) G[i].clear();
		int x,y,p;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&x,&y,&p);
			locp[i]=pii(x,y);
			poe[i]=p;
		}
		int xi,yi;
		int tot=0;
		for(int j=1;j<=m;j++)
		{
			f[j].id=j;
			scanf("%d%d%d%d%d%d",&f[j].x,&f[j].y,&f[j].lim,&f[j].pri,&f[j].r,&f[j].sta);
			if(f[j].sta==1) {tot+=f[j].pri;f[j].pri=-f[j].pri;}
		}
		sort(f+1,f+1+m);
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(f[i].lim<poe[j]) continue;
				int dist=dis(locp[j],f[i].x,f[i].y);
				if(dist<=f[i].r*f[i].r)
				{
					G[i+n].push_back(j);
					G[j].push_back(i+n);
				}
			}
		}
		if(hunger(n,m)!=n)
		{
			puts("-1");
			continue;
		}
		else 
		{
			int ans=0;
			memset(can,0,sizeof(can));
			for(int i=1;i<=n;i++)
			{
				ans+=f[linker[i]-n].pri;
				can[f[linker[i]-n].id]=1;
			}
			printf("%d\n",tot+ans);
			int cnt=0;
			for(int i=1;i<=m;i++)
			{
				if(can[i])
				printf("%d%c",i,++cnt==n?'\n':' ');
			}
		}
	}
}	
	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值