I In Case of an Invasion, Please. . .

这篇博客讨论了一个涉及无向图和避难所分配的问题。给定n个城市和k个能作为避难所的城市,每个城市有一定人口,每个避难所有一定的接纳能力。目标是最小化所有人到达最近避难所的最大时间。通过二分最大流的方法解决,预处理出从每个避难所到各城市的最短时间,然后利用优化技巧,如将状态相同的多个城市合并,降低复杂度。最后,通过二分查找确定最大时间的最小值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题 意 : 给 定 一 个 无 向 图 : n 个 城 市 , 其 中 有 k 个 城 市 h i 可 以 作 为 避 难 所 题意:给定一个无向图:n个城市,其中有k个城市h_i可以作为避难所 nkhi

每 个 城 市 有 p i 个 人 , 每 个 避 难 所 能 接 纳 的 人 为 c i , 现 在 问 n 个 城 市 的 所 有 人 每个城市有p_i个人,每个避难所能接纳的人为c_i,现在问n个城市的所有人 pici,n

到 达 避 难 所 的 最 大 时 间 最 小 为 多 少 到达避难所的最大时间最小为多少

分 析 : 很 明 显 , 二 分 最 大 流 , 那 么 , 怎 么 建 图 呢 , 经 过 分析:很明显,二分最大流,那么,怎么建图呢,经过 这一题

的 启 发 , 我 们 可 以 先 预 处 理 出 每 一 个 h i 到 各 个 城 市 的 最 短 时 间 的启发,我们可以先预处理出每一个h_i到各个城市的最短时间 hi

其 实 就 是 每 个 城 市 s i 到 避 难 所 h i 的 最 短 时 间 , 然 后 呢 , 二 分 最 短 时 间 其实就是每个城市s_i到避难所h_i的最短时间,然后呢,二分最短时间 sihi,

对 于 每 个 城 市 , 我 们 不 能 直 接 往 避 难 所 建 一 条 边 跑 最 大 流 , 这 显 然 会 T 对于每个城市,我们不能直接往避难所建一条边跑最大流,这显然会T T

怎 么 优 化 呢 , 我 们 发 现 k 值 很 小 , 那 么 对 于 每 个 城 市 最 多 有 2 k 种 状 态 怎么优化呢,我们发现k值很小,那么对于每个城市最多有2^k种状态 k2k

但 n 很 大 , 显 然 , 有 很 多 城 市 的 状 态 是 相 同 的 , 这 样 就 可 以 把 每 个 状 态 但n很大,显然,有很多城市的状态是相同的,这样就可以把每个状态 n

相 同 的 城 市 合 并 成 一 条 边 . . . 相同的城市合并成一条边... ...

c o d e : code: code

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
typedef unsigned long long ull;
#define Ios() ios::sync_with_stdio(false);
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int N = 3e6+5;
const int maxn = 1e5+10;
int head[maxn],tot;
struct E{
    int to,next; ll val;
}e[N],ee[N];
class Max_Flow
{
public:
    int s,t;
    int tot,head[maxn],dep[maxn],cur[maxn];
    Max_Flow(){tot=0;memset(head,-1,sizeof(head));}
    void clear(){tot=0;memset(head,-1,sizeof(head));}
    void add_e(int u,int v,ll w){
        e[tot].to=v,e[tot].next=head[u],e[tot].val=w;
        head[u]=tot++;
        e[tot].to=u,e[tot].next=head[v],e[tot].val=0;
        head[v]=tot++;
    }
    bool bfs(int S,int T){
        for(int i=0;i<maxn;i++) dep[i]=0,cur[i]=head[i];
        queue<int>q; q.push(S);
        dep[S]=1;
        while (!q.empty()){
            int x=q.front(); q.pop();
            for(int i=head[x];i!=-1;i=e[i].next){
                int y=e[i].to;
                if(dep[y]||!e[i].val) continue;
                dep[y]=dep[x]+1;
                q.push(y);
            }
        }
        return dep[T];
    }
    ll dfs(int x,int T,ll In){
        if(x==T) return In;
        ll Out=0;
        for(int i=cur[x];i!=-1;i=e[i].next){
            int y=e[i].to; cur[x]=i;
            if(dep[y]!=dep[x]+1||!e[i].val) continue;
            ll res=dfs(y,T,min(e[i].val,In-Out));
            e[i].val-=res,e[i^1].val+=res;
            Out+=res;
            if(In==Out) return Out;
        }
        if(In!=Out) dep[x]=0;
        return Out;
    }
    ll Dinic(){
        ll ans=0;
        while (bfs(s,t)) ans+=dfs(s,t,1e16);
        return ans;
    }
};

bool vis[maxn];
ll dis[20][maxn];
struct node{
    ll dis; int pos;
    bool operator<(const node &x)const{
        return x.dis<dis;
    }
};

int n,m,k;
ll a[maxn],h[maxn],c[maxn];

void add_e(int u,int v,ll w){
    ee[tot]={v,head[u],w};
    head[u]=tot++;
    ee[tot]={u,head[v],w};
    head[v]=tot++;
}

void dij(int s,int now)
{
    memset(vis,false,sizeof(vis));
    priority_queue<node>q; q.push({0,s});
    dis[now][s]=0;
    while (!q.empty())
    {
        int x=q.top().pos; q.pop();
        if(vis[x]) continue; vis[x]=true;
        for(int i=head[x];i!=-1;i=ee[i].next)
        {
            int y=ee[i].to;
            if(dis[now][y]>dis[now][x]+ee[i].val){
                dis[now][y]=dis[now][x]+ee[i].val;
                if(!vis[y]) q.push({dis[now][y],y});
            }
        }
    }
}

ll f[5005],sum;
bool check(ll mid)
{
    int Max=0;
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        int g=0;
        for(int j=1;j<=k;j++)
        {
            if(dis[j][i]>mid) continue;
            g+=1<<(j-1);
        }
        f[g]+=a[i]; Max=max(Max,g);
    }

    Max_Flow flow; flow.s=0,flow.t=Max+k+5;

    for(int i=0;i<=Max;i++) flow.add_e(flow.s,i,f[i]);

    for(int i=0;i<=Max;i++)
        for(int j=0;j<k;j++)
            if(i&(1<<j)) flow.add_e(i,Max+j+1,1e16);

    for(int i=1;i<=k;i++) flow.add_e(Max+i,flow.t,c[i]);

    if(flow.Dinic()==sum) return true;

    return false;
}

ll solve()
{
    for(int i=1;i<=k;i++) dij(h[i],i);

    ll l=0,r=1e16,ans=1e16;
    while (r>=l)
    {
        ll mid=(l+r)/2;
        if(check(mid))
            ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}

void Input()
{
    for(int i=0;i<=10;i++)
        for(int j=0;j<maxn;j++) dis[i][j]=1e16;
    memset(head,-1,sizeof(head));

    scanf("%d%d%d",&n,&m,&k);

    for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];

    for(int i=1;i<=m;i++){
        int u,v; ll w; scanf("%d%d%lld",&u,&v,&w);
        add_e(u,v,w);
    }
    for(int i=1;i<=k;i++) scanf("%lld%lld",&h[i],&c[i]);
}
int main()
{
    Input();
    printf("%lld\n",solve());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值