AtCoder Beginner Contest 142(补题)

E - Get Everything

链接: link.

题意:

N N N把需要解锁的宝箱,编号为 1 − N 1-N 1N,现在有 M M M把钥匙,每把钥匙价值 a i a_i ai元,每把钥匙可以解锁 b i b_i bi个宝箱,可以解锁的宝箱种类分别为 c i 1 , c i 2 . . . . c i b i c_{i1},c_{i2}....c_{ibi} ci1,ci2....cibi。问你花最少多少钱可以把所有钥匙都解锁

思路:

宝箱的数量较小,可以用2进制状态压缩来表示每个宝箱持有的状态。例如1号宝箱就是 00000001 00000001 00000001,2号就是00000010…依次往后。
现在假设你当前钥匙能开的宝箱的二进制是 n o w now now,当前的钥匙为 i i i号钥匙
定义 d p ( b i t ) dp(bit) dp(bit)为该二进制下解开这些宝箱所需要的最少钱数
那么此时 d p ( b i t ∣ n o w ) = m i n ( d p ( b i t ∣ n o w ) , d p ( b i t ) + a [ i ] ) dp(bit|now)=min(dp(bit|now),dp(bit)+a[i]) dp(bitnow)=min(dp(bitnow),dp(bit)+a[i]),初始状态 d p ( 0 ) = 0 dp(0)=0 dp(0)=0其他的状态初始化为 i n f inf inf,最终答案就是 d p ( ( 1 < < N ) − 1 ) dp((1<<N)-1) dp((1<<N)1)的值

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n, m;
int a[1111], b[1111];
int c[1111][20];
vector<int> dp(5050, inf);
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> a[i] >> b[i];
        for (int j = 1; j <= b[i]; j++) {
            cin >> c[i][j];
        }
    }
    dp[0] = 0;
    for (int i = 1; i <= m; i++) {
        int now = 0;
        for (int j = 1; j <= b[i]; j++) {
            now |= (1 << (c[i][j] - 1));
        }

        for (int bit = 0; bit < (1 << n); bit++) {
            dp[bit | now] = min(dp[bit | now], dp[bit] + a[i]);
        }
    }
    if (dp[(1 << n) - 1] == inf) {
        puts("-1");
    } else {
        cout << dp[(1 << n) - 1] << endl;
    }
    return 0;
}

F - Pure

链接: link.

题意:

给定一张 N N N个点和 M M M 条边的有向连通图,保证没有重边和自环。现在要找出一个子图,使得子图内每个点的入度和出度都恰好是 1 1 1。输出这个子图。

思路:

子图的每个点的入度和出度都恰好是1,那就说明要找一个环,且这个环中的每个点都是出去一条边,也进来一条边,那么此时只需要找图中的最小环即可,如果不是最小环,可能内部有些点的出度或者入读不是恰好为1,而是 > 1 >1 >1,只有最小环一定是出度入读恰好为1,这个可以用反证法简单证明。找最小环,直接用dfs来跑即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 2222, M = N * 2;
int h[N], e[M], ne[M], idx;
int d[N];
int minv = 0x3f3f3f3f;
int ans[N];
int temp[N];
int n, m;
void add_edge(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }

void dfs(int u, int root, int dis) {
    temp[dis] = u;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == root) {
            if (minv > d[u] - d[root] + 1) {
                minv = d[u] - d[root] + 1;
                int id = 0;
                for (int k = d[root]; k <= dis; k++) {
                    ans[id++] = temp[k];
                }
            }
        } else if (!d[j]) {
            d[j] = d[u] + 1;
            dfs(j, root, dis + 1);
        }
    }
}
int main() {
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    while (m--) {
        int a, b;
        cin >> a >> b;
        add_edge(a, b);
    }

    for (int i = 1; i <= n; i++) {
        memset(d, 0, sizeof(d));
        d[i] = 1;
        dfs(i, i, 1);
    }

    if (minv == 0x3f3f3f3f)
        puts("-1");
    else {
        cout << minv << endl;
        for (int i = 0; i < minv; i++) {
            cout << ans[i] << endl;
        }
    }
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值