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':' ');
}
}
}
}