hihocoder1136-DAG和DP的关系

题目描述

Professor Q develops a new software. The software consists of N modules which are numbered from 1 to N. The i-th module will be started up by signal Si. If signal Si is generated multiple times, the i-th module will also be started multiple times. Two different modules may be started up by the same signal. During its lifecircle, the i-th module will generate Ki signals: E1, E2, …, EKi. These signals may start up other modules and so on. Fortunately the software is so carefully designed that there is no loop in the starting chain of modules, which means eventually all the modules will be stoped. Professor Q generates some initial signals and want to know how many times each module is started.

思路

其实这题就是在描述算法导论上的一个思想,两个进程之间通过进程-信号-进程的方式向连接,如果这个图存在环的话,整个进程会永远无法停止,反之则存在可能停止.
所以,我们发现,如果A-B两个进程通过这样的方式向连,那么我们想要求出B进程的启动次数,就需要先求出A进程的启动次数,也就是逆DAG进行DP。转移方程如下:

DP[i]=DP[j],forjcantriggeri

这样,就可以解决这个问题了.

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
using namespace std;

#define MAXN 100005
const int MOD = 142857;

map<int,vector<int> >msi;
map<int,vector<int> > ism;
vector<int> start;
int dp[MAXN];
bool vis[MAXN];
int s[MAXN];
int t,n,m;

void Init()
{
    msi.clear();
    ism.clear();
    start.clear();
    memset(dp,0,sizeof(dp));
    memset(vis,false,sizeof(vis));
    return;
}

int Dfs(int cur)
{
    if(!vis[cur])vis[cur]=true;
    else return dp[cur];

    int i;
    int w = s[cur];

    for(i=0;i<ism[w].size();i++){
        int tmp2 = ism[w][i];
        dp[cur] = (dp[cur] + Dfs(tmp2))%MOD;
    }

    return dp[cur];
}

int main()
{
    //freopen("input","r",stdin);
    int i,j;
    int tmp1,tmp2;

    scanf("%d",&t);

    while(t--){
        Init();
        scanf("%d%d",&n,&m);

        for(i=0;i<m;i++){
            scanf("%d",&tmp1);
            start.push_back(tmp1);
        }

        for(i=0;i<n;i++){
            scanf("%d %d",&tmp1,&tmp2);
            msi[tmp1].push_back(i);
            s[i] = tmp1;
            for(j=0;j<tmp2;j++){
                scanf("%d",&tmp1);
                ism[tmp1].push_back(i);
            }
        }

        for(i=0;i<start.size();i++){
            tmp1 = start[i];
            for(j=0;j<msi[tmp1].size();j++){
                tmp2 = msi[tmp1][j];
                dp[tmp2]++;
            }
        }

        for(i=0;i<n;i++){
            if(!vis[i])Dfs(i);
        }

        for(i=0;i<n;i++){
            printf("%d%c",dp[i],i==n-1?'\n':' ');
        }

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值