zzuli 2527: THE END IS COMING!!!!!//最小费用最大流

zzuli 2527: THE END IS COMING!!!!!//最小费用最大流

时间限制: 1 Sec  内存限制: 256 MB
提交: 24  解决: 7
[提交] [状态] [讨论版] [命题人:外部导入]

题目描述

    几年之前,大地的守护者耐萨里奥承受不住上古之神恩佐斯长时间的侵蚀,失去了理智,自称死亡之翼。 只有一种只存在于传说中的神器“巨龙之魂”才能帮助我们压制它的力量。你在一次冒险中意外发现了它, 所以,拯救世界的重担就扛在了你的肩上。
    使用巨龙之魂的第一步就是启动它。巨龙之魂由很多个机关构成,只有解开所有的机关我们才能使用它。
    在巨龙之魂的表面拥有一个坐标系统,两个机关之间的距离就是他们之间的曼哈顿距离。
    每个机关会在一个特定的时间开启,你必须在他开启的一瞬间开始解锁它(否则它就会崩坏),每个机关需 要一段时间来解锁。 
    你有 m 种元素,最多五种,不同的元素不能代替彼此。元素们由巨龙之魂中的一个洞送进来。 
    每个机关有自己的元素需求。比如第 i 个机关需要 cij 个 j 号元素来解锁。只有拥有了所有它需要的元素, 你才能开始解锁它。
    每个元素可以解锁无数个机关。也就是说,只要它在解锁完上一个机关时能够及时赶到下一个机关,它就 可以参与下一个机关的解锁。元素的移动速度是 1。
    有一些机关距离输送元素的洞太远了导致我们根本无法解锁它。红龙女王阿莱克斯塔萨赐予了我们生命的 力量,可以直接激发 k 个机关,如果我们还是有机关无法激活,那我们就无法阻止死亡之翼了。
    元素们都十分狂野并且难以控制,所以你需要用尽可能少的元素来激活巨龙之魂,最少多少个呢? 

输入

第一行三个数字,n(n<=100) ,m(m<=5) 和 k(k<=n/2),分别是机关的数量(包括输送元素的洞),元素 的种类,和可以直接激发的机关的数量。
第二行两个数字,输送元素的洞所在的坐标。 
接下来 n−1 行,每行信息如下所示: 
x y st ut c1 c2 ··· c 
x 和 y 代表这个机关的位置,st 是这个机关开启的时间,ut 是解锁这个机关需要的时间,c1-m 是每种元 素需要的数量。 
ci<=10 剩余的数字都小于 4e4。 

输出

一个数字,最少需要的元素数量。 
如果你无法激活巨龙之魂,你需要输出”THE END IS COMING!!!!!”(不含引号)

 

样例输入 Copy

4 2 2
0 0
1 0 1 1 3 3
1 1 3 1 2 2
0 1 2 10 1 1

样例输出 Copy

8

提示

首先我们有 3 个机关,有两个可以被直接激活。有两种元素可以被使用到。 
元素是从 (0,0) 被输送进来的。 
第 1 个机关在 (1,0) 位置,在第 1 分钟开启,需要 1 分钟来解锁,需要 3 个 1 号元素和 3 个 2 号元素来解 锁。 
第 2 个机关在 (1,1) 位置,在第 3 分钟开启,需要 1 分钟来解锁,需要 2 个 1 号元素和 2 个 2 号元素来解 锁。 
第 3 个机关在 (0,1) 位置,在第 2 分钟开启,需要 10 分钟来解锁,需要 1 个 1 号元素和 1 个 2 号元素来 解锁。 
用于解锁第一个机关的元素可以在第二个机关开启前赶过去,所以我们总共需要 6 个元素来解锁这两个机 关。 
第三个机关只能用新的元素来解锁,因为 12 机关的元素都赶不过去。。。 
所以我们总共需要 8 个元素来解锁所有的机关。

 题意:

有n个机关,每个机关需要m种元素,每种元素需要ci个,元素从起点走到每个机关,花费uti时间打开机关后可以走到其他的机关,当元素数量满足前述条件时,求出最少的元素数量。

题解:

1.我们只有 5 种元素,所以可以全部分开考虑每种元素单独考虑
2.每个点拆成两部分,一个用于接受元素,一个用于往外送元素。
3.源点向每个点的接受元素部分建立流量为当前节点需要元素数量,费用为 1 的边。
4.接受元素部分向用于外送元素的部分建立流量为当前节点需要元素数量,费用为 0 的边。
5.外送元素部分向所有能及时赶到的接受元素部分建立流量为正无穷,费用为 0 的边,意味着同一个元素又去了其他地方。
6.每个接受元素的部分向超级汇点建立流量为当前节点需要元素数量,费用为 0 的边,意味着这个点接收到了足够多的元素。
7.每个离起点距离不支持的点记录数量,超过 k 就是无解。
8.处理过程中跳过这些点就好了

样例对应的图:

注意 :.k个生命的力量只能用于肯定不能处理的机关,即从一开始就排元素也不能处理的机关(坑的地方、、、、不知道zzujuju怎么和出题人心意相通的)

解释:

发现每个点到汇点只有一条路(紫色),而紫色对应的是左边一列的点。

左边一列的点可以又两部分转移来:

1:直接源点过来(红色),那么就是必须消耗的元素。而答案就是必须消耗的减去可以白嫖的。

2:源点到右边一列到左边一列(粉->蓝)这部分cost是0,说明可以白嫖。

然后这就是一个最大流下求一个最小费用流,那么稍微最小费用流改一下即可。

思路挺巧的,,,要我肯定暴毙。。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int max_n = 505;
int n,m,k;
int x[max_n],y[max_n],st[max_n],ut[max_n],c[max_n][10];
bool vis[max_n];
struct no{int to,cap,cost,rev;};
struct qno{int v,d;}; //队列节点
bool operator<(const qno &x,const qno &y) {return x.d>y.d;} //队列cmp重写
vector<no>g[max_n];
int dis[max_n],h[max_n]; //最短距离,顶点的势
int prevv[max_n],preve[max_n]; //在vector下记录路径
void addedge(int from,int to,int cap,int cost){
    g[from].push_back((no){to,cap,cost,g[to].size()});
    g[to].push_back((no){from,0,-cost,g[from].size()-1});
}
int min_cost_flow(int s,int t,int f){//注意n变化了!!!!!!!!!!!!!!!!!
    int res=0; //answer
    for(int i=0;i<=n;i++) h[i]=0; //初始化h
    while(f>0){
        priority_queue<qno>q;
        for(int i=0;i<=n;i++) dis[i]=INT_MAX/2;
        dis[s]=0;
        q.push((qno){s,0});
        while(!q.empty()){
            qno now=q.top();q.pop();
            int v=now.v;
            if(dis[v]<now.d) continue;
            for(int i=0;i<g[v].size();i++){
                no &e=g[v][i];
                if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ //加入啦势
                    dis[e.to]=dis[v]+e.cost+h[v]-h[e.to];
                    prevv[e.to]=v;
                    preve[e.to]=i;
                    q.push((qno){e.to,dis[e.to]});
                }
            }
        }
        if(dis[t]==INT_MAX/2) break;
        for(int v=0;v<=n;v++) h[v]+=dis[v];
        int d=f; //找增广路
        for(int v=t;v!=s;v=prevv[v]) d=min(d,g[prevv[v]][preve[v]].cap);
        f-=d;res+=d*h[t];
        for(int v=t;v!=s;v=prevv[v]){
            no &e=g[prevv[v]][preve[v]];
            e.cap-=d;
            g[v][e.rev].cap+=d;
        }
    }
    return res;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);;
    scanf("%d%d",&x[0],&y[0]);
    int cnt=0;
    for(int i=1;i<n;i++) {
        scanf("%d%d%d%d",&x[i],&y[i],&st[i],&ut[i]);
        int mhd=abs(x[i]-x[0])+abs(y[i]-y[0]);
        if(mhd>st[i]) {cnt++;vis[i]=1;} //不可能处理的机关
        for(int j=1;j<=m;j++) {
            scanf("%d",&c[i][j]);
        }
    }
    if(cnt>k) {printf("THE END IS COMING!!!!!");return 0;}
    int ans=0;
    for(int i=1;i<=m;i++){
        for(int j=1;j<n;j++){
            if(vis[j]) continue;
            addedge(0,j,c[j][i],1);//原本需要的
            addedge(j,j+n,c[j][i],0);
            addedge(0,j+n,c[j][i],0);
            for(int k=1;k<n;k++){
                if(j==k) continue;
                if(vis[k]) continue;
                int mhd=abs(x[j]-x[k])+abs(y[j]-y[k]); //白嫖的
                if((st[j]+mhd+ut[j])<=st[k])
                    addedge(j+n,k,INT_MAX/2,0);
            }
            addedge(j,2*n,c[j][i],0);
        }
        n=n*2;
        int x=min_cost_flow(0,n,INT_MAX/2);
        ans=ans+x;
        for(int i=0;i<=n+1;i++) g[i].clear();
        n=n/2;
    }
    printf("%d",ans);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值