POJ-3498-March of the Penguins

378 篇文章 0 订阅

这个题是说有一些冰块,每个冰块上都有些企鹅,同时每个冰块上企鹅只能跳k次,给出企鹅能跳的最大距离,需要求出企鹅能在那些冰块上相遇。

思路:

1、建立初始点和汇点,枚举每个点作为聚结点。

2、拆分点(将A拆分为A和A',若AB能够相连,则分别连接A’->B,B'->A,权值赋为无穷。将起点与每个点建立连接,权值为每个冰块最开始所有的企鹅数,将每个点与它的拆点进行连接,权值为该冰块所允许的最大跳跃次数,然后对于枚举的汇聚点,修改该点与拆点的权值为无穷,然后再将它与汇点连接,权值为无穷。

3、建图完毕后,用Dinic/ISAP跑网络流即可,若得到的值等于企鹅的数量,则该点可以作为汇聚点。

代码:


#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
const int inf=1<<29;
const int maxn=210;
const int maxm=5e5;
struct Pole
{
    int x;
    int y;
    int s;
    int w;
}a[110];
int n,e,st,des,head[maxn],pnt[maxm],nxt[maxm],flow[maxm],level[maxn];
double d,dis[110][110];
queue<int> q;
double Dis(int i,int j)
{
    return sqrt(1.0*(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
}
void AddEdge(int u,int v,int f)
{
    pnt[e]=v;nxt[e]=head[u];flow[e]=f;head[u]=e++;
    pnt[e]=u;nxt[e]=head[v];flow[e]=0;head[v]=e++;
}
bool BFS()
{
    memset(level,0,sizeof(level));
    level[st]=1;
    q.push(st);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=nxt[i])
            if(flow[i]&&!level[pnt[i]])
            {
                level[pnt[i]]=level[u]+1;
                q.push(pnt[i]);
            }
    }
    return level[des];
}
int DFS(int u,int maxf)
{
    if(u==des||!maxf)
        return maxf;
    for(int i=head[u],t;i!=-1;i=nxt[i])
        if(level[pnt[i]]==level[u]+1&&(t=DFS(pnt[i],min(maxf,flow[i]))))
        {
            flow[i]-=t;
            flow[i^1]+=t;
            return t;
        }
    return level[u]=0;
}
int maxflow()
{
    int ans=0;
    while(BFS())
        while(1)
        {
            int val=DFS(st,inf);
            if(!val)
                break;
            ans+=val;
        }
    return ans;
}
void Build(int val)
{
    e=st=0;des=2*n+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
    {
        AddEdge(st,i,a[i].s);
    }
    for(int i=1;i<=n;i++)
    {
        if(val==i)
            AddEdge(i,n+i,inf);
        else
            AddEdge(i,n+i,a[i].w);
        for(int j=i+1;j<=n;j++)
            if(dis[i][j]<=d)
            {
                AddEdge(n+i,j,inf);
                AddEdge(n+j,i,inf);
            }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int sum=0;
        scanf("%d%lf",&n,&d);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].s,&a[i].w);
            sum+=a[i].s;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++)
                 dis[i][j]=dis[j][i]=Dis(i,j);
        bool is=true,first=true;
        for(int i=1;i<=n;i++)
        {
            Build(i); 
            AddEdge(n+i,des,inf);
            int ans=maxflow();
            if(ans==sum)
            {
                is=false;
                if(first)
                {
                    printf("%d",i-1);
                    first=false;
                }
                else
                    printf(" %d",i-1);
                
            }
        }
        if(is)
            printf("-1");
        printf("\n");
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值