AGC31E Snuke the Phantom Thief

Snuke the Phantom Thief

有 \(N\) 个珠宝,第 \(i\) 个位于 \((x_i, y_i)\),价值为 \(v_i\)。你可以选择一些珠宝,有若干限制,每个限制形如如下四种之一:

  • \(x ≤ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
  • \(x ≥ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
  • \(y ≤ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
  • \(y ≥ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;

最大化选择的总价值。

\(1 ≤ N ≤ 80,1 ≤ x_i, y_i, a_i≤ 100\)。

题解

https://www.cnblogs.com/zhoushuyu/p/10548483.html

首先这数据范围看着就很费用流

先考虑一维怎么做。

一个很妙的转化是:限制横坐标 \(≤a_i\) 的珠宝里至多选 \(b_i\) 个,等价于选择的横坐标第 \(b_{i+1}\) 小的珠宝,其横坐标必须 \(>a_i\)。

如果是限制横坐标 \(≥a_i\) 的珠宝至多选 \(b_i\) 个,则可以先枚举选 \(s\) 个珠宝,然后限制就等价于选择的第 \(s-b_i\) 个珠宝其横坐标必须 \(<a_i\)。(前述只考虑 \(b_i < s\) 的限制,\(b_i ≥ s\) 的限制显然无效)

这样我们就可以得到 \(s\) 个二元组 \([l_j,r_j]\),分别表示第 \(j\) 个珠宝的横坐标的范围限制。注意这 \(s\) 个二元组的 \(l\) 和 \(r\) 应满足单调不降。

这样我们就得到了一个匹配的模型:二分图一侧有 \(s\) 个点,另一侧有 \(n\) 个点,满足范围限制的点之间连边,然后求一组最大权匹配即可。

至于二维的问题,可以直接把图拆成三份,即左侧 \(s\) 个点表示横坐标的限制,中间拆 \(2n\) 个点内部连权值的边表示珠宝,右侧另 \(s\) 个点表示纵坐标的限制。

#include<bits/stdc++.h>
using namespace std;

#define CO const
#define IN inline
typedef long long int64;

template<class T> IN T read(){
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;
}
template<class T> IN T read(T&x){
    return x=read<T>();
}

CO int N=400;
CO int64 inf=1e18;
namespace flow{
    int n,S,T;
    struct edge {int v,c;int64 w;int a;};
    vector<edge> to[N];
    int64 dis[N];int vis[N];

    IN void init(int n){
        flow::n=n,S=n-1,T=n;
        for(int i=1;i<=n;++i) to[i].clear();
    }
    IN void link(int u,int v,int c,int64 w){
        to[u].push_back({v,c,w}),to[v].push_back({u,0,-w});
        to[u].back().a=to[v].size()-1,to[v].back().a=to[u].size()-1;
    }
    bool bfs(){
        fill(dis+1,dis+n+1,-inf),dis[T]=0;
        deque<int> Q={T};vis[T]=1;
        while(Q.size()){
            int u=Q.front();
            Q.pop_front(),vis[u]=0;
            for(CO edge&e:to[u])if(to[e.v][e.a].c){
                if(dis[e.v]<dis[u]-e.w){ // edit 1: -w
                    dis[e.v]=dis[u]-e.w;
                    if(vis[e.v]) continue;
                    if(Q.size() and dis[e.v]>=dis[Q.front()])
                        Q.push_front(e.v);
                    else Q.push_back(e.v);
                    vis[e.v]=1;
                }
            }
        }
        return dis[S]>-inf;
    }
    int dfs(int u,int lim){
        if(u==T) return lim;
        vis[u]=1;
        int rest=lim;
        for(edge&e:to[u])if(!vis[e.v] and e.c and dis[e.v]==dis[u]-e.w){
            int delta=dfs(e.v,min(e.c,rest));
            if(!delta) {dis[e.v]=-inf;continue;}
            rest-=delta,e.c-=delta,to[e.v][e.a].c+=delta;
            if(!rest) break;
        }
        vis[u]=0;
        return lim-rest;
    }
    int64 main(){
        int64 ans=0;
        while(bfs()) ans+=dfs(S,1e9)*dis[S];
        return ans;
    }
}

int n,X[N],Y[N];int64 V[N];
int m,O[N],A[N],B[N];
int L[N],R[N],D[N],U[N];

int64 solve(int s){
    fill(L+1,L+s+1,0),fill(D+1,D+s+1,0);
    fill(R+1,R+s+1,233),fill(U+1,U+s+1,233); // edit 2: range of XY
    for(int i=1;i<=m;++i)if(B[i]<s){
        if(O[i]=='L') L[B[i]+1]=A[i]+1;
        else if(O[i]=='R') R[s-B[i]]=A[i]-1;
        else if(O[i]=='D') D[B[i]+1]=A[i]+1;
        else U[s-B[i]]=A[i]-1;
    }
    for(int i=2;i<=s;++i){
        L[i]=max(L[i],L[i-1]);
        D[i]=max(D[i],D[i-1]);
    }
    for(int i=s-1;i>=1;--i){
        R[i]=min(R[i],R[i+1]);
        U[i]=min(U[i],U[i+1]);
    }
    flow::init(2*n+2*s+2);
    for(int i=1;i<=n;++i) flow::link(i,i+n,1,V[i]);
    for(int i=1;i<=s;++i){
        flow::link(flow::S,i+2*n,1,0),flow::link(i+2*n+s,flow::T,1,0);
        for(int j=1;j<=n;++j){
            if(L[i]<=X[j] and X[j]<=R[i]) flow::link(i+2*n,j,1,0);
            if(D[i]<=Y[j] and Y[j]<=U[i]) flow::link(j+n,i+2*n+s,1,0);
        }
    }
    return flow::main();
}
int main(){
    read(n);
    for(int i=1;i<=n;++i) read(X[i]),read(Y[i]),read(V[i]);
    read(m);
    for(int i=1;i<=m;++i){
        char opt[2];scanf("%s",opt);
        O[i]=opt[0],read(A[i]),read(B[i]);
    }
    int64 ans=0;
    for(int i=1;i<=n;++i) ans=max(ans,solve(i));
    printf("%lld\n",ans);
    return 0;
}

好久不写费用流都写不对了……

原文地址:https://www.cnblogs.com/autoint/p/12204074.html

时间: 2024-10-29 08:14:41

AGC31E Snuke the Phantom Thief的相关文章

【HDOJ】1983 Kaitou Kid - The Phantom Thief (2)

不仅仅是DFS,还需要考虑可以走到终点.同时,需要进行预处理.至多封闭点数为起点和终点的非墙壁点的最小值. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <queue> 6 using namespace std; 7 8 typedef struct node_st { 9 int x, y, t, f

hdu 1983 Kaitou Kid - The Phantom Thief (2)

bfs+dfs很有意思也很好的一道题 然而我用了很久才ac #include<iostream> #include<cstring> #include<queue> using namespace std; char mapp[10][10]; int visit[10][10]; int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; int n,m,o; int sx,sy,ex,ey; int flag; struct stu { i

HDU 1983 Kaitou Kid - The Phantom Thief (2) bfs and dfs

Description 破解字迷之后,你得知Kid将会在展览开始后T分钟内盗取至少一颗宝石,并离开展馆.整个展馆呈矩形分布,划分为N*M个区域,有唯一的入口和出口(不能从出口进入,同样不能从入口出去).由某个区域可直接移动至相邻四个区域中的一个,且最快需要一分钟.假设Kid进入放有宝石的区域即可盗取宝石,无需耗时.问至少要封锁几个区域(可以封锁放有宝石的区域,但不能封锁入口和出口)才能保证Kid无法完成任务. Input 输入的第一行有一个整数C,代表有C组测试数据.每组测试数据的第一行有三个整

BFS专题

题目编号 OJ编号 题目名称 题解链接 Problem A HDU 1548 A strange lift http://www.cnblogs.com/ohyee/p/5389459.html Problem B HDU 1372 Knight Moves http://www.cnblogs.com/ohyee/p/5389471.html Problem C HDU 2717 Catch That Cow http://www.cnblogs.com/ohyee/p/5389479.htm

50道hdu基础搜索总结(转)

Dfs: 大部分是直接递归枚举,即求满足约束条件下的解,虽不用剪枝,但也需要代码能力. 练习递归枚举的题目: 1241       Oil Deposits (dfs的连通块个数) 1016       Prime Ring Problem 1584       蜘蛛牌(简单dfs,简单的剪枝,还有人用DP做(???)) 1426       Sudoku Killer(练习递归的好题目 or Dancing links(???)) 2510       符号三角形(打表题,写写打表程序还是不错

搜索题推荐

(转自网络博客): POJ POJ 1376 – Robot(基础) http://acm.pku.edu.cn/JudgeOnline/problem?id=1376 题意:略 解法:bfs,A*-. POJ 2688 – Cleaning Robot(基础) http://acm.pku.edu.cn/JudgeOnline/problem?id=2688 题意:bfs后转换为tsp问题 解法:bfs+dp,bfs+dfs 相关:http://hi.baidu.com/zfy0701/blo

HDU 专题分类

[背包问题] 2602 Bone Collector 1114 Piggy-Bank 1203 I NEED A OFFER! 1171 Big Event in HDU 1059 Dividing 2844 Coins 2191 悼念512汶川大地震遇难同胞--珍惜现在,感恩生活 2159 FATE 1561 The more, The Better 1011 Starship Troopers 2639 Bone Collector II 3033 I love sneakers! 2955

ARC078 D.Fennec VS. Snuke(树上博弈)

题目大意: 给定一棵n个结点的树 一开始黑方占据1号结点,白方占据n号结点 其他结点都没有颜色 每次黑方可以选择黑色结点临近的未染色结点,染成黑色 白方同理. 最后谁不能走谁输. 题解: 其实简单想想就可以想明白. 黑方肯定要往通往白方的最短路延伸,白方也是这样. 因为这样每次你可以最大化可行动次数. 所以先以1为根,dfs一遍,然后找到路径. 模拟一下走路径的过程,路径走光了就比谁的可行动次数多(有点像围棋的气的感觉),输出结果就可以了 #include <iostream> #includ

Fennec VS. Snuke

Fennec VS. Snuke Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statement Fennec and Snuke are playing a board game. On the board, there are N cells numbered 1 through N, and N−1 roads, each connecting two cells. Cell ai is adjac