CSU1580: Outing(图论+DP)

Description

Input

Output

Sample Input

4 4
1 2 3 4

Sample Output

4

HINT

Source

NCPC 2014


题意:有n个人,车上有k个座位

然后n个输入,a[i],代表i坐车的先决条件是a[i]必须坐车


思路:这道题是用的图论的方法,图论是我队友主攻,我主要是提供了一些思路

首先,对于成环的情况,这换里面的人都是可以上车的

那么我们把环看成一个点,那么就能形成几条链,那么这些人都是可以上车的

其次,我们要考虑座位的数量,因为有环的话,对于这条链,必然是以环开头,环不满足的话,整条链都不能满足,例如环满足需要四个座位,但是车上只有三个座位,那么这整条链是不满足的


下面的代码是队友的,有爱的同学自己慢慢理解吧。。。


#include<stdio.h>
#include<vector>
#include<string.h>
using namespace std;
const int N = 1005;
 
int dfn[N],low[N],Stack[N],flag[N],vist[N],num[N],top,deep,tn;
vector<int>mapt1[N],mapt2[N];
 
void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        mapt1[i].clear();
        mapt2[i].clear();
        dfn[i]=0;
        num[i]=0;
        vist[i]=0;
    }
    tn=top=deep=0;
}
int tt;
void dfs(int u)
{
    deep++; vist[u]=tt;
    dfn[u]=low[u]=deep;
    Stack[++top]=u;
    int len=mapt1[u].size();
    for(int i=0;i<len;i++)
    {
        int v=mapt1[u][i];
 
        if(vist[v]==0)
        {
            dfs(v);
            if(low[u]>low[v])
                low[u]=low[v];
        }
        else if(vist[v]==tt&&low[u]>dfn[v])
            low[u]=dfn[v];
    }
    if(low[u]==dfn[u])
    {
        tn++;
        while(u!=Stack[top])
        {
            flag[Stack[top]]=tn; num[tn]++;top--; 
        }
        flag[Stack[top]]=tn; num[tn]++; top--;
 
    }
}
int in[N],have[N][N];
void rebuilMap(int n)
{
    tt=0;
    for(int i=1;i<=n;i++)
        if(vist[i]==0)
        {
            tt++;
            dfs(i);
        }
 
    memset(in,0,sizeof(in));
    memset(have,0,sizeof(have));
 
    for(int i=1;i<=n;i++)
    {
        int u=flag[i];
        for(int j=0;j<mapt1[i].size();j++)
        {
            int v=flag[mapt1[i][j]];
            if(u==v||have[v][u])
                continue;
            in[u]++; have[v][u]=1;
            mapt2[v].push_back(u);
        }
    }
}
int k;
int solve()
{
    int dp[N],a[N],m=0;
    memset(dp,0,sizeof(dp));
    dp[0]=1;
    for(int i=1;i<=tn;i++)
    if(in[i]==0&&num[i]<=k)
    {
        a[m++]=i;
        for(int j=k;j>=num[i];j--)
        if(dp[j-num[i]]==1)
            dp[j]=1;
    }
    while(m--)
    {
        int u=a[m];
        for(int i=0;i<mapt2[u].size();i++)
        {
            int v=mapt2[u][i];
            in[v]--;
            if(in[v]==0&&num[v]<=k)
            {
                a[m++]=v;
                for(int j=k;j>=num[v];j--)
                if(dp[j-num[v]]==1)
                    dp[j]=1;
            }
        }
    }
    for(int i=k;i>=0;i--)
        if(dp[i])
        return i;
    return 0;
}
int main()
{
    int n;
    scanf("%d%d",&n,&k);
    {
        init(n);
        for(int i=1;i<=n;i++)
        {
            int j;
            scanf("%d",&j);
            mapt1[i].push_back(j);
        }
        rebuilMap(n);
        printf("%d\n",solve());
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值