• 很好的脑洞题:dfs+暴力 Gym


    http://codeforces.com/gym/101128

    题目大意:给你一个a,b,e,p。有e个点,p条有向边,每条边为(x,y),表示x->y,每次我们都取出一个入度为0的,并且一次性取出来的个数为a(或b)。当然,取出来的种类可能有很多种(即一个集合),问,这个集合中有多少个数字是相同的。

    第一个输出集合长度为a的,第二个输出集合长度为b的,第三个输出无论如何都无法被取出的个数。

    思路:建立正向图和反向图。

    定义pair<int, int> interval[i] 表示第i个节点能在[L, R]的任意步走到,L表示first,R表示second

    首先for(0~e),暴力目前节点是i

    然后我们向上dfs一次,表示走到当前节点至少需要几步。这个就是左端点

    然后我们再从上到下进行bfs,每次如果能走,那么step就++,

    然后无法bfs了,就是最终的step,就是右端点
    然后就是最后的三个判断了

    if (interval[i].se <= a) ans1++;
    if (interval[i].se <= b) ans2++;
    if (interval[i].fi > b) ans3++;

    orz,想了3个小时(刚开始以为可能存在环,所以还特地写了强连通,后来发现,去掉强连通了也AC了,一口老血)

    跑了960ms

    //看看会不会爆int!数组会不会少了一维!
    //取物问题一定要小心先手胜利的条件
    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define ALL(a) a.begin(), a.end()
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    #define haha printf("haha
    ")
    const int maxn = 5000 + 5;
    int a , b , e , p;
    vector<int> edge[maxn];
    vector<int> disedge[maxn];///反向
    int in[maxn], tmpin[maxn];
    vector<int> zero;
    int step;
    bool vis[maxn], can[maxn];
    int cnt[maxn];
    pair<int, int> interval[maxn];
    
    void dfs(int u){
        vis[u] = true;
        step++;
        for (int i = 0; i < disedge[u].size(); i++){
            int v = disedge[u][i];
            if (vis[v]) continue;
            dfs(v);
        }
    }
    
    void solve(){
        for (int i = 0; i < e; i++){
            step = 1;
            for (int j = 0; j < e; j++) vis[j] = false, tmpin[j] = in[j];
            vis[i] = true;
            for (int j = 0; j < disedge[i].size(); j++){
                int v = disedge[i][j];
                if (!vis[v]) dfs(v);
            }
            interval[i].fi = step;
            queue<int> que;
            for (int j = 0; j < zero.size(); j++){
                if(zero[j] != i) que.push(zero[j]);
            }
            while (!que.empty()){
                queue<int> tmp;
                while (!que.empty()){
                    int pos = que.front(); que.pop();
                    if (!vis[pos]) step++;
                    for (int j = 0; j < edge[pos].size(); j++){
                        int v = edge[pos][j];
                        if (v == i) continue;
                        tmpin[v]--;
                        if (tmpin[v] == 0) tmp.push(v);
                    }
                }
                que = tmp;
            }
            interval[i].se = step;
        }
        int ans1 = 0, ans2 = 0, ans3 = 0;
        for (int i = 0; i < e; i++){
            if (interval[i].se <= a) ans1++;
            if (interval[i].se <= b) ans2++;
            if (interval[i].fi > b) ans3++;
        }
        printf("%d
    %d
    %d
    ", ans1, ans2, ans3);
    }
    
    int main(){
        while (scanf("%d%d%d%d", &a, &b, &e, &p) == 4){
            zero.clear();
            for (int i = 0; i < e; i++) {
                in[i] = 0, can[i] = true;
                edge[i].clear(), disedge[i].clear();
            }
            for(int i = 0 ; i < p ; ++ i){
                int u, v;
                scanf("%d%d" , &u , &v);
                edge[u].push_back(v);
                disedge[v].push_back(u);
                in[v]++;
            }
            for (int i = 0; i < e; i++){
                if (in[i] == 0) zero.push_back(i);
            }
            solve();
        }
        return 0;
    }
    View Code

    看了一下大佬的想法,果然是我比较zz:大佬的链接

    对于第一个输出和第二个输出,他们的右端点就是n-num[i],其中num[i]表示i节点所支配的节点的个数,当n-num[i] <= x的时候,i节点必然选择。

    然后对于第三种的话,定义他是节点i,那么我们就是看看节点i什么时候“解封”(即可以选择),那么我们对于所有的能到节点i的节点的num都加在一起(建立反向图即可),那么所有的num和如果大于b,就++即可

  • 相关阅读:
    c语言命名规则 [转载]
    [转贴]C编译过程概述
    [转贴]漫谈C语言及如何学习C语言
    Semaphore源码分析
    如何快速转行大数据
    web前端到底怎么学?
    Code Review怎样做好
    SDK与API的理解
    分析消费者大数据
    程序员的搞笑段子
  • 原文地址:https://www.cnblogs.com/heimao5027/p/6715889.html
Copyright © 2020-2023  润新知