Gym - 100502 G - Outing

题目链接:http://codeforces.com/gym/100502/attachments。

题意:有n个人旅游,第i个人去的前提是第a[i]个人去,车上最多载k人,最多可以多少人去旅游。

思路:先跑tarjan把强连通块弄出来,然后在树上背包也就是树形dp,具体转移方法比较复杂,看代码吧:

#include<iostream>    
#include<cmath>    
#include<queue>    
#include<cstdio>    
#include<queue>    
#include<algorithm>    
#include<cstring>    
#include<string>    
#include<utility>  
#include<map>  
#include<vector>  
#define maxn 200005   
#define E 1005  
#define V 1005  
#define inf 0x3f3f3f3f    
using namespace std;  
typedef long long LL;  
const double eps = 1e-8;  
struct edge  
{  
    int to, next;  
}Edge[E];  
int head[V], e, n;  
int indeg[V]; //联通块的入度和出度数  
int belong[V], low[V], dfn[V], scc, cnt, num[V], connect[V][V];//dfn[]:遍历到u点的时间; low[]:u点可到达的各点中最小的dfn[v];belong[]:该点所属的联通块;scc:联通块个数;  
int S[V], top;//S[]//数组栈  
bool vis[V];//v是否在栈中  
int dp[V][V], k;  
vector<int>p[V];  
int addedge(int u, int v)  
{  
    Edge[e].to = v;  
    Edge[e].next = head[u];  
    head[u] = e++;  
    return 0;  
}  
void tarjan(int u)  
{  
    int v;  
    dfn[u] = low[u] = ++cnt;//开始时dfn[u] == low[u]  
    S[top++] = u;//不管三七二十一进栈  
    vis[u] = true;  
    for (int i = head[u]; i != -1; i = Edge[i].next)  
    {  
        v = Edge[i].to;  
        if (dfn[v] == 0)//如果v点还未遍历  
        {  
            tarjan(v);//向下遍历  
            low[u] = low[u] < low[v] ? low[u] : low[v];//确保low[u]最小  
        }  
        else if (vis[v] && low[u] > dfn[v])//v在栈中,修改low[u]  
            low[u] = dfn[v];  
    }  
    if (dfn[u] == low[u])//u为该强连通分量中遍历所成树的根  
    {  
        ++scc;  
        do  
        {  
            v = S[--top];//栈中所有到u的点都属于该强连通分量,退栈  
            vis[v] = false;  
            belong[v] = scc;  
            num[scc]++;  
        } while (u != v);  
    }  
  
}  
  
int solve()  
{  
    for (int u = 1; u <= n; ++u)  
        if (!dfn[u])  
            tarjan(u);  
    return scc;  
}  
void dfs(int now){  
    dp[now][0] = dp[now][num[now]] = 1;  
    for (int i = 0; i < p[now].size(); i++){  
        dfs(p[now][i]);  
        for (int j = k; j > num[now]; j--){  
            if (dp[now][j])  
                continue;  
            for (int l = 0; l <= j - num[now]; l++){  
                if (dp[now][j - l] && dp[p[now][i]][l]){  
                    dp[now][j] = 1;  
                }  
            }  
        }  
    }  
}  
int main(){  
    memset(head, -1, sizeof(head));  
    scanf("%d%d", &n, &k);  
    for (int i = 1; i <= n; i++){  
        int u;  
        scanf("%d", &u);  
        addedge(u, i);  
    }  
    solve();  
    for (int i = 1; i <= n; i++){  
        for (int j = head[i]; ~j; j = Edge[j].next){  
            if (belong[i] == belong[Edge[j].to]||connect[belong[i]][belong[Edge[j].to]])  
                continue;  
            connect[belong[i]][belong[Edge[j].to]] = 1;  
            p[belong[i]].push_back(belong[Edge[j].to]);  
            indeg[belong[Edge[j].to]]++;  
        }  
    }  
    for (int i = 1; i <= scc; i++){  
        if (!indeg[i])  
            p[0].push_back(i);  
    }  
    dfs(0);  
    for (int i = k; i >= 0; i--){  
        if (dp[0][i]){  
            printf("%d\n", i);  
            break;  
        }  
    }  
}  




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值