【WF2017】Mission Improbable

http://www.lydsy.com/JudgeOnline/problem.php?id=4950

对于俯视图很好解决,把所有不是0的位置拿到剩1就可以了。

对于正视图与侧视图,稍微想一下也能发现只要保持每行(和每列)箱子最多的那个位置不动就可以了。

但是可能存在一行有两个以上的点都是最大值,并且其中一点所在的列的最大值也和这行的最大值相等的情况。这时候只保持这一点不变,显然优于同时保持该行另一点和那一列的最大值那点不变要更优。

这时候就可以建二分图找最大匹配了:将若i行与j列的最大值相等且不为零,且(i,j)原本有箱子,就在i与r+j连边。

最后统计一下答案就可以了。

#include <iostream>
#include <cstring>
#define maxn 105
using namespace std;
int r, c, grid[maxn][maxn];
int rmax[maxn], cmax[maxn];
struct
{
    int to, next;
} edges[maxn * maxn];
int head[maxn * 2];
void addedge(int u, int v)
{
    static int ecnt = 1;
    edges[ecnt].to = v;
    edges[ecnt].next = head[u];
    head[u] = ecnt++;
}
bool vis[maxn * 2];
int mat[maxn * 2];
bool dfs(int v)
{
    for (int i = head[v]; i; i = edges[i].next)
    {
        int w = edges[i].to;
        if (!vis[w])
        {
            vis[w] = true;
            if (!mat[w] || dfs(mat[w]))
            {
                mat[w] = v;
                mat[v] = w;
                return true;
            }
        }
    }
    return false;
}
void hungary()
{
    for (int i = 1; i <= r; i++)
    {
        if (!mat[i])
        {
            memset(vis, false, sizeof(vis));
            dfs(i);
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    unsigned long long ans = 0;
    cin >> r >> c;
    for (int i = 1; i <= r; i++)
    {
        for (int j = 1; j <= c; j++)
        {
            cin >> grid[i][j];
            if (grid[i][j])
            {
                rmax[i] = max(rmax[i], grid[i][j]);
                cmax[j] = max(cmax[j], grid[i][j]);
                ans += grid[i][j] - 1;
            }
        }
    }

    // 若i行与j列的最大值相同,就可以把位置(i,j)放上这个最大值的数量的箱子,然后将i行与j列的其他能偷的箱子全部偷走
    // 但是若(i,j)原来是0,这个位置就不能放箱子了
    for (int i = 1; i <= r; i++)
        for (int j = 1; j <= c; j++)
            if (rmax[i] == cmax[j] && rmax[i] && grid[i][j])
                addedge(i, j + maxn);
    hungary();

    for (int i = 1; i <= r; i++)
        if (rmax[i])
            ans -= rmax[i] - 1;
    for (int i = 1; i <= c; i++)
        if (!mat[i + maxn] && cmax[i])
            ans -= cmax[i] - 1;
    cout << ans;
    return 0;
}
时间: 2024-10-14 21:55:26

【WF2017】Mission Improbable的相关文章

【UVA11795】 Mega Man&#39;s Mission

题面 你要杀n个怪,每杀掉一个怪那个怪会掉落一种武器,这种武器可以杀死特定的怪.游戏初始你有一把武器,能杀死一些怪物.每次只能杀一只,求有多少种杀怪方法.n≤16 分析 f[i]的i的二进制表示每个怪物是否被杀死,f[i]的值表示目前这种状态的方案数.最后答案为f[(1<<n)-1]同时用s[i]表示状态为i的怪已经被杀了后得到的武器能杀死哪些怪.转移:如果这些怪在i状态时未被杀且i状态的武器能够杀死这个怪,就把这些怪杀了 代码 #include<bits/stdc++.h> us

【POJ】2449 Remmarguts&#39; Date(k短路)

http://poj.org/problem?id=2449 不会.. 百度学习.. 恩. k短路不难理解的. 结合了a_star的思想.每动一次进行一次估价,然后找最小的(此时的最短路)然后累计到k 首先我们建反向边,跑一次从汇到源的最短路,将跑出来的最短路作为估价函数h 根据f=g+h 我们将源s先走,此时实际价值g为0,估价为最短路(他们的和就是s-t的最短路) 将所有s所连的边都做相同的处理,加入到堆中(假设此时到达的点为x,那么x的g等于s到这个点的边权,因为根据最优,g+h此时是从x

POJ3249 Test for Job 【DAG】+【记忆化搜索】

Test for Job Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 9201   Accepted: 2080 Description Mr.Dog was fired by his company. In order to support his family, he must find a new job as soon as possible. Nowadays, It's hard to have a job

【POJ】【2449】Remmarguts&#39; Date

K短路/A* 经(luo)典(ti) K短路题目= = K短路学习:http://www.cnblogs.com/Hilda/p/3226692.html 流程: 先把所有边逆向,做一遍dijkstra,得到估价函数h(x)(x到T的最短路距离) f(x)=g(x)+h(x) 按f(x)维护一个堆……T第k次出堆时的g(T)即为ans 另外,需要特判:如果S==T,k++ 1 Source Code 2 Problem: 2449 User: sdfzyhy 3 Memory: 11260K T

POJ 2449 Remmarguts&#39; Date【SPFA】【A*】

Remmarguts' Date Time Limit: 4000MS Memory Limit: 65536K Total Submissions: 21978 Accepted: 5982 Description "Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly touching his little ducks' head, he to

【Kettle】4、SQL SERVER到SQL SERVER数据转换抽取实例

1.系统版本信息 System:Windows旗舰版 Service Pack1 Kettle版本:6.1.0.1-196 JDK版本:1.8.0_72 2.连接数据库 本次实例连接数据库时使用全局变量. 2.1 创建新转换:spoon启动后,点击Ctrl+N创建新转换 2.2 在新转换界面中,右键点击DB连接,系统会弹出[数据库连接]界面. windows系统环境下,可用${}获取变量的内容. 说明: 连接名称:配置数据源使用名称.(必填) 主机名称:数据库主机IP地址,此处演示使用本地IP(

详解go语言的array和slice 【二】

上一篇  详解go语言的array和slice [一]已经讲解过,array和slice的一些基本用法,使用array和slice时需要注意的地方,特别是slice需要注意的地方比较多.上一篇的最后讲解到创建新的slice时使用第三个索引来限制slice的容量,在操作新slice时,如果新slice的容量大于长度时,添加新元素依然后使源的相应元素改变.这一篇里我会讲解到如何避免这些问题,以及迭代.和做为方法参数方面的知识点. slice的长度和容量设置为同一个值 如果在创建新的slice时我们把

【转载】C++拷贝构造函数(深拷贝,浅拷贝)

对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #include <iostream>using namespace std;class CExample {private:     int a;public:     CExample(int b)     { a=b;}     void Show ()     {        cout<

【BZOJ】1799: [Ahoi2009]self 同类分布

[题意]给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数.1 ≤ a ≤ b ≤ 10^18 [算法]数位DP [题解] 感觉这种方法很暴力啊. 枚举数位和1~162(不能枚举0,不然会模0,相当于除0),记忆化f[pos][sum][val],sum表示当前数位和,val表示数字取模枚举的数位和. 每次sum+i和(val*10+i)%MOD转移. sum用减法优化,即记忆化(MOD-sum),但是枚举过程中都要memset,导致效率低下,记忆化效果很差. 要什么方法才能跑1.3s