首页 > 其他分享 >POJ 1548 Robots

POJ 1548 Robots

时间:2023-08-01 09:34:15浏览次数:47  
标签:idx int Robots 1548 ++ POJ vec alls include

\(POJ\) \(1548\) \(Robots\)

题意

相当于给出\(N\)个坐标点,因为机器人只能向下或者向右走,所以如果能到达其他点,则连接这两个点,即line[i][j]=1

最小路径覆盖数:
对于一个\(DAG\)(有向无环图),选取最少条路径,使得每个 顶点属于且仅属于一条路径。路径长度可以为零;(有向图中找一些路径,使之覆盖了图中的所有顶点,就是任意一个顶点都跟那些路径中的某一条关联,且任何一个顶点有且只有一个与之关联)

最小路径覆盖数(最少边覆盖)=顶点数-最大匹配数
思路:把每个点都拆成两个点,分为入点和出点,如果 \(u\) 到 \(v\) 有边,那么我们就让 \(u\) 的入点连向 \(v\) 的出点 , 匈牙利算出最大匹配

解答
最小路径覆盖的话非常简单,这题显然可以转化为\(DAG\)。要注意类似\(floyd\)求传递闭包的办法,排序后把符合条件的全部建边

不过也可以使用\(LIS\),运用\(dilworth\)定理,也就是求最长反链的长度。也就是求最长下降子数列的长度

二分图建边+匈牙利算法

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

typedef pair<int, int> PII;
#define x first
#define y second
// 链式前向星
const int N = 1010, M = N * N;

int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

// 匈牙利算法
int st[N], match[N];
int dfs(int u) {
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (st[v] == 1) continue;
        st[v] = 1;
        if (match[v] == -1 || dfs(match[v])) {
            match[v] = u;
            return 1;
        }
    }
    return 0;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("POJ1548.in", "r", stdin);
#endif
    int a, b;
    while (true) {
        // 初始化匈牙利算法的数组
        memset(match, -1, sizeof match);
        memset(st, 0, sizeof st);

        // 初始化链式前向星
        memset(h, -1, sizeof h);
        idx = 0;

        vector<PII> vec;
        while (true) {
            cin >> a >> b;
            if (a == 0 && b == 0) break;
            if (a == -1 && b == -1) exit(0);
            vec.push_back(make_pair(a, b)); // POJ太老了,我喜欢用vec.push_back({a,b});但它不认识
        }

        // 按x1<=x2,y1<=y2逻辑进行排序
        // 总结:给了一大堆点,我们需要按一定顺序进行排序,然后再枚举遍历。一般的排序办法就是PII的默认排序办法
        sort(vec.begin(), vec.end());

        /* 每个机器人可以从左->右,上->下,走完就废。
         我们上面进行了排序,是按x1<=x2,y1<=y2排序的,但可能出现(x2>x1,y2<y1)的情况,也就是后面的行,但列在前面
         这是不符合题意的,不能连边,需要判断一下,即y要非单调上升,也就是y2>=y1
        */
        for (int i = 0; i < vec.size(); i++)
            for (int j = i + 1; j < vec.size(); j++)
                if (vec[j].y >= vec[i].y) add(i, j);
        // 记录哪个点有出边,这个建边,是不是类似于floyd求传递闭包?多么痛的领悟!

        // 匈牙利算法
        int res = 0;

        /* 注意:这里需要枚举的上限是vec.size()!原因是上面我们建边时用的是add(i,j),也就是在链式前向星中保存的是原始点集中的点号!一开始黄海错误的把下面的循环终止条件写成了i<=idx,结果TLE,这是不对的!有两个原因:
           ① 因为除非你在建图时使用了离散化,否则点号不全!!因为有的点号因为不符合vec[j].y>=vec[i].y的条件,也就是在
           下一行的左侧,被排除掉了,但它的号被占着呢!
           ② idx是边数,不是点数,SB到家了!

           我又写了一个离散化后的版本,但代码太长了,不好玩,不如直接枚举vec.size()来的快,这样,即使有的点不符合条件,也就没有出边,不影响结果!
           代码短的多!
        */

        for (int i = 0; i < vec.size(); i++) {
            memset(st, 0, sizeof st);
            if (dfs(i)) res++;
        }
        // 最小路径覆盖数 = 节点总数 - 最大匹配数
        printf("%d\n", vec.size() - res);
    }
    return 0;
}

离散化后的版本

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

typedef pair<int, int> PII;
#define x first
#define y second

const int N = 1010, M = N * N;

int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

int st[N], match[N];
int dfs(int u) {
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (st[v] == 1) continue;
        st[v] = 1;
        if (match[v] == -1 || dfs(match[v])) {
            match[v] = u;
            return 1;
        }
    }
    return 0;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("POJ1548.in", "r", stdin);
#endif
    int a, b;
    while (true) {
        memset(match, -1, sizeof match);
        memset(st, 0, sizeof st);

        memset(h, -1, sizeof h);
        idx = 0;

        vector<PII> vec;
        while (true) {
            cin >> a >> b;
            if (a == 0 && b == 0) break;
            if (a == -1 && b == -1) exit(0);
            vec.push_back(make_pair(a, b));
        }

        sort(vec.begin(), vec.end());

        vector<int> alls; // 存储所有待离散化的值
        for (int i = 0; i < vec.size(); i++)
            for (int j = i + 1; j < vec.size(); j++)
                if (vec[j].y >= vec[i].y) alls.push_back(i), alls.push_back(j);

        // 将所有值排序
        sort(alls.begin(), alls.end());
        // 去掉重复元素
        alls.erase(unique(alls.begin(), alls.end()), alls.end());

        // 重新捋着建边,通过二分查找,找出新的序号,这样就可以使用i<=alls.size()了!
        for (int i = 0; i < vec.size(); i++)
            for (int j = i + 1; j < vec.size(); j++)
                if (vec[j].y >= vec[i].y) {
                    int x = lower_bound(alls.begin(), alls.end(), i) - alls.begin();
                    int y = lower_bound(alls.begin(), alls.end(), j) - alls.begin();
                    add(x, y);
                }

        int res = 0;

        for (int i = 0; i < alls.size(); i++) {
            // 最开始黄海SB的以为二分后,这里可以使用i<idx做为终止条件,其实是SB到家了!idx是边数,是边数,是边数!
            // 不是点数,不是点数,不是点数!
            memset(st, 0, sizeof st);
            if (dfs(i)) res++;
        }

        printf("%d\n", vec.size() - res);
    }
    return 0;
}

\(LIS+Dilworth\)定理

其实也可以用\(LIS\)来解决。把所有点按第一关键字\(x\),第二关键字\(y\)从小到大排序。
则从\(1\sim n\)的点已经满足了第一维,从\(i->j(i< j)\)只需要满足\(a[i].y<=a[j].y\)即可。

例如样例就是\(4 \ 4 \ 6 \ 4 \ 7\).根据题意,这个偏序集的链是一个不降序列。
我们现在就是要求这个偏序集的最小链划分数(也就是图中的最小链覆盖数),
根据\(Dilworth\)定理,也就是求最长反链的长度。也就是求最长下降子数列的长度。可以\(dp\)解决。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>

using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second

const int N = 600;
int ans, f[N];
PII a[N];

int main() {
#ifndef ONLINE_JUDGE
    freopen("POJ1548.in", "r", stdin);
#endif
    while (true) {
        memset(f, 0, sizeof f);
        ans = 0;
        int al = 0;
        while (true) {
            int x, y;
            scanf("%d %d", &x, &y);
            if (x == -1) exit(0);
            if (x == 0) break;
            a[al++] = make_pair(x, y);
        }

        sort(a, a + al);

        // LIS
        for (int i = 0; i < al; i++)
            for (int j = 0; j < i; j++)
                if (a[j].y > a[i].y) f[i] = max(f[i], f[j] + 1);
        // Dilworth
        for (int i = 0; i < al; i++) ans = max(ans, f[i]);
        printf("%d\n", ans + 1);
    }
    return 0;
}

标签:idx,int,Robots,1548,++,POJ,vec,alls,include
From: https://www.cnblogs.com/littlehb/p/17595613.html

相关文章

  • POJ1904 King's Quest
    \(POJ1904\)\(King's\)\(Quest\)一、题目描述有\(n\)个王子,每个王子都有\(k\)个喜欢的妹子,每个王子只能和喜欢的妹子结婚。现有一个匹配表,将每个王子都与一个自己喜欢的妹子配对。请你根据这个表得出每个王子可以和几个自己喜欢的妹子结婚,按序号升序输出妹子的编号,这个表应满......
  • poj 2886 Who Gets the Most Candies? (线段树单点更新应用)
                           poj2886WhoGetstheMostCandies?DescriptionNchildrenaresittinginacircletoplayagame.Thechildrenarenumberedfrom1toNinclockwiseorder.Eachofthemhasacardwithanon-zerointegeronit......
  • POJ 3694 Network
    POJ3694Network一、题目大意\(n\)个点,\(m\)个边,连通图。点与点之间通过边连接,如果切断某个边使得有点与其他点连接断开(连通分支增加),则称这种边为桥梁(离散上叫割边)。接下来有\(Q\)个操作,每操作一次,也就是切断某条边后,输出当前存在的桥梁数量。二、样例分析我们看这个4......
  • (Relax 数论1.26)POJ 1496 Word Index(计算一个字符串在字典中的位置)
    大致题意:(与POJ1850基本一致)输出某个str字符串在字典中的位置,由于字典是从a=1开始的,因此str的位置值就是在str前面所有字符串的个数+1规定输入的字符串必须是升序排列。不降序列是非法字符串要求用循环输入,输入若干组字符串,若输入非法字符串则输出0,但不结束程序,这是和POJ1850......
  • POJ 1458 Common Subsequence(动态规划)
    传送门代码如下:#include<iostream>#include<cstdio>usingnamespacestd;intmaxLen[1000][1000];intmain(){strings1,s2;while(cin>>s1>>s2){intlength1=s1.length();intlength2=s2.length();......
  • 题解 POJ3318【Matrix Multiplication】
    postedon2022-10-2119:56:08|under题解|sourceproblem判断三个\(n\timesn\)的矩阵是否满足\(A\timesB=C\),\(n\leq500\)。solution随机一个行向量\(v\)。若\(a\timesb=c\),则有\(v\timesa\timesb=v\timesc\)(不充分)。显然相乘复杂度仅为\(O(n^2)\)。类似于......
  • poj 1716 Integer Intervals (贪心)
    题意:给定n个连续的区间,求一个集合。其中,n个区间的每一个区间都至少包含两个这个集合的元素。求这个集合元素的最小值。 题解:1、先将所有区间按终点从小到大排序。2、我们先取第一个区间(排好序的区间)的两个值,因为要使得结果最优,我们肯定选择最接近终点的那两个值。假设一个为Selem,......
  • poj 1844 sum (数学)
    题意:给出一个数S,从1到N个数,每个数前面可以是负号或者是正号,这样累加起来,结果可以等于S,问最小的N是多少。题解:因为从1一直加到n的值(假设为sum(n))等于sum的n是最小的。所以我们先算出sum(n)大于等于sum的那个n。这样我们可以得出一个值m=sum(n)-sum.如果m==0那么n就是我们要求的......
  • poj 2311 Cutting Game (sg函数)
    小记:这题是对sg函数的初步理解。对于sg函数只要g[x]==0,则此时为必败态。x作为后继,我们就要对所有的后继进行标记,然后mex之。因为每次只能切一刀,所以切完之后,会有两块方格,而对每一块方格进行游戏又会有一个sg函数值,所以根据sg函数的性质,它这一刀所代表的后继,即为这两块方格的sg函......
  • poj 1632 Vase collection
    题意:有n个花瓶,每个花瓶都带有两种属性-形状和颜色,而每种属性都有36种不同的状态。求最大的k,使得k*k个花瓶的形状和颜色都有k种状态,且k*k个花瓶的两种属性都是由形状和颜色的k种状态组合而成的。题解:我们用一个数组(comb[])存放形状和颜色,数组的下标为形状,然后将颜色状态压缩成为数组......