Wi-fi Towers(2009 Google Jam world final D)最大权闭合子图

Wi-fi Towers(2009 Google Jam world final D)最大权闭合子图

题目链接:传送门

题意

​ 给定一个无线电塔的网络。对于每座无线电塔,都有一个半径参数,这座无线电塔可以给这个半径范围内的其他无线电塔发送信号,刚开始时无线电塔之间都使用的是古老的协议A进行通信,现在要将某些电塔升级到协议B,升级有个要求:如果电塔a升级到协议B,那么电塔a范围内所有电塔都必须升级到协议B(如果仅仅是某个协议B的电塔a被电塔b覆盖,那么电塔b不用必须升级到协议B)

​ 考虑到升级后的花费和升级后的收益,现我们给每座无线电塔打一个分数(正分表示升级之后收益更多,负分表示升级的花费更多),现要选择一些合适的无线电塔进行升级,使得升级的无线电塔的总分最大。

假设无线电塔的个数为 n n n,无线电塔 i i i的位置为 ( x i , y i ) (x_i,y_i) (xi,yi),覆盖半径为 r i r_i ri,分数是 s i ​ s_i​ si.

数据范围: n ∈ [ 1 , 500 ] , x , y ∈ [ − 1000 , 1000 ] , r ∈ [ 1 , 2000 ] , s ∈ [ − 1000 , 1000 ] n\in[1,500],x,y\in[-1000,1000],r\in[1,2000],s\in[-1000,1000] n[1,500],x,y[1000,1000],r[1,2000],s[1000,1000]

思路

​ 所以我们让源点S连向 w i ≥ 0 \mathbf{w}_{\mathbf{i}}\mathbf{\geq 0} wi0的点i,容量为 w i \mathbf{w}_{\mathbf{i}} wi;让 w i < 0 \mathbf{w}_{\mathbf{i}}\mathbf{<0} wi<0的点i连向汇点T,容量为 − w i \mathbf{-}\mathbf{w}_{\mathbf{i}} wi;对于原图的边 ( u , v ) \mathbf{(u,v)} (u,v),让u连向v容量为inf。最终 su m + − ​ \mathbf{\text{su}}\mathbf{m}_{\mathbf{+}}\mathbf{-}​ sum+(S到T的最小割)就是答案。

代码

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=550;
const int inf=0x3f3f3f3f;
struct edge{
int cap,rev,to;
    edge(){};
    edge(int to,int cap,int rev){this->to=to,this->cap=cap,this->rev=rev;}
};
class EK{
public:
    vector<edge> adja[maxn];
    int prevv[maxn],top;
    int preve[maxn];
    void init(int n)
    {
        for(int i=0;i<n;++i)    adja[i].clear();
        top=n;
    }
    void bfs(int s,int t)
    {
        memset(prevv,-1,sizeof(prevv));
        queue<int> mmp;
        mmp.push(s);
        prevv[s]=s;
        while(!mmp.empty()){
            int u=mmp.front();
            mmp.pop();
            for(int i=0;i<adja[u].size();++i){
                edge& e=adja[u][i];
                if(prevv[e.to]==-1&&e.cap>0)
                {
                    prevv[e.to]=u;
                    preve[e.to]=i;
                    mmp.push(e.to);
                }
            }
        }
    }
    void addEdge(int u,int v,int f)
    {
        adja[u].push_back(edge(v,f,adja[v].size()));
        adja[v].push_back(edge(u,0,adja[u].size()-1));
    }
    int maxFlow(int s,int t)
    {
        int flow=0;
        for(;;)
        {
            bfs(s,t);
            if(prevv[t]==-1)
                return flow;
            else
            {
                int minn=inf;
                for(int last=t;prevv[last]!=last;last=prevv[last]){
                    minn=min(minn,adja[prevv[last]][preve[last]].cap);
                }
                flow+=minn;
                for(int last=t;prevv[last]!=last;last=prevv[last]){
                    edge& e=adja[prevv[last]][preve[last]];
                    e.cap-=minn;
                    adja[last][e.rev].cap+=minn;
                }
            }
        }
    }
}ooo;
const int N=505;
int x[N],y[N],r[N],s[N];
int solve(int n)
{
    int S=0,T=n+1,sum=0;
    ooo.init(n+2);
    for(int i=1;i<=n;++i)
    {
        if(s[i]>0){
            sum+=s[i];
            ooo.addEdge(S,i,s[i]);
        }
        else{
            ooo.addEdge(i,T,-s[i]);
        }
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j){
            if(i==j) continue;
            int m=(x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i]);
            if(m<=r[i]*r[i]){
                ooo.addEdge(i,j,inf);
            }
        }
    }
    int flow=ooo.maxFlow(S,T);
    return sum-flow;
}
int main()
{
//    freopen("1.in","r",stdin);
//    freopen("1.out","w",stdout);
    int t,cas=0;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%d%d%d%d",x+i,y+i,r+i,s+i);
        printf("Case #%d: %d\n",++cas,solve(n));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值