日常训练 20170628 神奇的玩具

题意简述: n 个玩具,m 家店买这些玩具,只能进 k 个店,要每种玩具各买一份并且代价最小,求最小总价格。
数据范围: 1m40 ,1n100 ,0pi,qi107
题解: 一开始想暴搜,结果看周围人都纷纷网络流,我思索了一下,脑补了一个看起来很对的费用流,对于每个店建一个流量为 1 费用为一个很大的数的边,然后再建一条流量 n 费用为 0 的边,强制先流哪条费用大的边,退流时后退那条边,结果死循环了,DJY大佬说费用函数斜率要非负,我实在是太蠢了。
   正解居然真的是暴搜,姿势不同得分也很不同,因为 n 比较大,所以要搜 m <script type="math/tex" id="MathJax-Element-1944">m</script> ,而且搜索时加一些必要性剪枝比如一件商品在前一个店没买在这个店肯定要买,再加上最优性剪枝(要加估价不然会多剪到最优解)。

#include<bits/stdc++.h>
typedef long long ll;
const int N = 105;
struct rec{
    int v, _x, _v, next;
} mp[N * 2];
int n, m, k, first[N], s;
ll f[N], ans;
void ins(int x, int v, int _x, int _v) {
    mp[++s] = (rec) {v, _x, _v, first[x]};
    first[x] = s;
}
void dfs(int dep, int pick, ll S, ll now) {
    if (now - f[dep] > ans) return;
    if (dep == m) {if (now < ans) ans = now; return;}
    bool can_not_choose = 1;
    for (int t = first[dep]; t; t = mp[t].next)
        if (mp[t]._x <= dep && !(S & (1ll << mp[t]._x))) {can_not_choose = 0; break;}
    if (can_not_choose) dfs(dep + 1, pick, S, now);
    if (pick == k) return;
    for (int t = first[dep]; t; t = mp[t].next) {
        if (mp[t]._x >= dep || !(S & (1ll << mp[t]._x)))
            now += mp[t].v;
        else
            if (mp[t].v < mp[t]._v)
                now -= mp[t]._v - mp[t].v;
            else;
    }
    dfs(dep + 1, pick + 1, S ^ (1ll << dep), now);
}
int main() {
    freopen("toy.in", "r", stdin);
    freopen("toy.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &k);
    int a, p, b, q;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d", &a, &p, &b, &q); a--, b--;
        if (a == b) p = q = std::min(p, q);
        ins(a, p, b, q); if (a != b) ins(b, q, a, p);
        if (a > b) std::swap(a, b), std::swap(p, q);
        if (q < p) for (int i = a; i <= b; i++) f[i] += p - q;
    }
    ans = 1e15;
    dfs(0, 0, 0, 0);
    if (ans == (ll)1e15)
        printf("-1\n");
    else
        printf("%lld\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值