hiho 1271 舰队游戏 状压dp 贪心 模拟

33 篇文章 0 订阅
21 篇文章 0 订阅

题目

题目链接:http://hihocoder.com/problemset/problem/1271

题目来源:hiho的比赛。

简要题意:给定对空和对舰的飞机还有一个航母,给定计算公式,要你弄个奇怪的方案。

题解

题意非常扭曲,但是的确算是个好题吧。

对于 30% 的数据,可以进行搜索,枚举每个位置放哪个飞机。

对于大数据可以采用状压dp的方式。

最多 16 个位置,状态空间为 216

由于题目中的限制,你无法直接去算整个结果。

可以分别计算两边的最大值然后再进行一个最后的判断

dp[i][j] 表示前 i 个对?飞机,位置使用状况为j的最大对?伤害。

搞完之后根据题目之中的条件进行一个判断就可以了。

需要注意的是如果直接这么做还是无法通过所有数据的。

可以发现任何位置对同种的伤害越大越好

于是可以排序,只留下最大的 n×m 个对空,对舰的然后去做。

开始直接搞,没贪心各种WA,TLE一晚上,第二天醒来就想明白了。

题目本身还是非常不错的,代码量还是有点长。

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
// head
const int N = 10;
const int T = 1005;
const int M = 1 << 16;
const int INF = -1;

int a[N][N];
vector<int> b, c;

vector<PII> d;
int dp[2][M];
int res[2][M];

bool exist = false;
bool yes = false;
int ans = 0;

int n, m, t, s, tt, x, nm, mx;

bool ck(int st) {
    int temp[5];
    memset(temp, 0, sizeof temp);
    for (int i = 0; i < (n*m); i++) {
        if ((1 << i) & st) {
            temp[d[i].fi] = true;
        }
    }
    for (int i = 0; i < n; i++) {
        if (!temp[i]) return false;
    }
    return true;
}

void getRes(int *tar, const vector<int> &v) {
    memset(dp, INF, sizeof dp);
    dp[0][0] = 0;
    int cur = 1, pre = 0;
    for (int i = 0; i < v.size(); i++) {
        for (int j = 0; j < mx; j++) {
            if (dp[pre][j] == INF) continue;
            dp[cur][j] = max(dp[cur][j], dp[pre][j]);
            for (int k = 0; k < nm; k++) {
                if ((1 << k) & j) continue;
                int nxt = j | (1<<k);
                dp[cur][nxt] = max(dp[cur][nxt], dp[pre][j] + a[d[k].fi][d[k].se] * v[i]);
            }
        }
        swap(cur, pre);
    }
    memcpy(tar, dp[pre], sizeof dp[pre]);
}

void solve() {
    getRes(res[0], b);
    getRes(res[1], c);
    for (int i = 0; i < mx; i++) {
        if (res[0][i] < s) continue;
        exist = true;

        int cur = (mx-1) ^ i;
        if (res[1][cur] < ans) continue;

        if (res[1][cur] > ans) {
            ans = res[1][cur];
            yes = false;
        }
        if (ck(cur)) {
            yes = true;
        }
    }
}

void input(vector<int> &b) {
    for (int i = 0; i < t; i++) {
        scanf("%d", &x);
        if (x) b.push_back(x);
    }
    sort(b.begin(), b.end());
    reverse(b.begin(), b.end());
    while (b.size() > d.size()) {
        b.pop_back();
    }
}

int main() {
    scanf("%d", &tt);
    while (tt--) {
        scanf("%d%d%d%d", &n, &m, &t, &s);
        nm = n * m;
        mx = 1 << nm;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                scanf("%d", a[i]+j);
                d.push_back(make_pair(i, j));
            }
        }
        input(b);
        input(c);

        exist = yes = false;
        ans = 0;
        solve();
        if (!exist) {
            puts("Not Exist");
        } else {
            printf("%d\n%s\n", ans, (yes ? "Yes" : "No"));
        }
        d.clear();
        b.clear();
        c.clear();
    }
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值