P4580 [BJOI2014]路径

简简单单,没人写题解,我来,(我不知道如何发题解,大神们帮个忙。。。)

题目描述

在一个 N 个节点的无向图(没有自环、重边)上,每个点都有一个符号,可能是数字,也可能是加号、减号、乘号、除号、小括号。你要在这个图上数一数,有多少种走恰好 K 个节点的方法,使得路过的符号串起来能够得到一个算数表达式。路径的起点和终点可以任意选择。

所谓算数表达式,就是由运算符连接起来的一系列数字。括号可以插入在表达式中以表明运算顺序。

注意,你要处理各种情况,比如数字不能有多余的前导 0,减号只有前面没有运算符或数字的时候才可以当成负号,括号可以任意添加(但不能有空括号),0可以做除数(我们只考虑文法而不考虑语意),加号不能当正号。

例如,下面的是合法的表达式:

-0/0
((0)+(((2*3+4)+(-5)+7))+(-(2*3)*6))

而下面的不是合法的表达式:

001+0
1+2(2)
3+-3
--1
+1
()

输入格式

第一行三个整数N,M,K,表示点的数量,边的数量和走的节点数。

第二行一个字符串,表示每个点的符号。

接下来M行,每行两个数,表示一条边连的两个点的编号。

1≤N≤20,0≤M≤N×(N-1)/2,0≤K≤30

输出格式

输出一行一个整数,表示走的方法数。这个数可能比较大,你只需要输出它模1000000007的余数即可。

输入输出样例

输入 #1复制

6 10 3
)(1*+0
1 2
1 3
1 4
2 3
3 4
2 5
3 5
3 6
4 6
5 6

输出 #1复制

10

说明/提示

一共有10条路径,构成的表达式依次是101, (1), 1+1, 1+0, 1*1, 1*0, 0+0, 0+1, 0*0, 0*1

上代码

#include <iostream>
#include <cstring>
#include <string>

using namespace std;

const int MAXN = 40;
const int MOD = 1000000007;

int N,M,K;
char s[30];

bool is_digit(char c)
{
    return c >= '0' && c <= '9';
}

bool is_operator(char c)
{
    return c == '+' || c == '-' || c == '*' || c == '/';
}

int g[MAXN][MAXN];
int dp[MAXN][MAXN][MAXN][2];


int dfs(int u, int k, int p, int z)
{
    if(dp[u][k][p][z] >= 0)
        return dp[u][k][p][z];
    int ret = 0;
    if(k == K){
        if((p==0) && !is_operator(s[u]))
            ret = 1;
        else 
            ret = 0;
        return dp[u][k][p][z] = ret; 
    }
    for(int v = 0; v < N; ++v){
        if(g[u][v]){
            
            if(is_digit(s[v])){
                if(is_digit(s[u]) && !z){
                    ret += dfs(v, k+1, p, 0);
                    ret %= MOD;
                }
                else if(is_operator(s[u]) || s[u] == '('){
                    ret += dfs(v, k+1, p, s[v] == '0');
                    ret %= MOD;
                }
            }
            else if(s[v] == '('){
                if(s[u] == '(' || is_operator(s[u])){
                    ret += dfs(v, k+1, p+1, 0);
                    ret %= MOD;
                }
            }
           
            else if(s[v] == ')'){
                
                if(p > 0 && (is_digit(s[u]) || s[u] == ')')){
                    ret += dfs(v, k+1, p-1, 0);
                    ret %= MOD;
                }
            }
           
            else {
                if(is_digit(s[u])){
                    ret += dfs(v, k+1, p, 0);
                    ret %= MOD;
                }
                if(s[u] == ')' || (s[u] == '(' && s[v] == '-')){
                    ret += dfs(v, k+1, p, 0);
                    ret %= MOD;
                }
            }
        }
    }
    return dp[u][k][p][z] = ret;
}

int main()
{
    
    cin >> N >> M >> K;
    cin >> s;

    memset(g,0,sizeof(g));
    memset(dp, -1, sizeof(dp));

    for(int i = 1; i <= M; ++i){
        int u,v;
        cin >> u >> v;
        u--;
        v--;
        g[u][v] = g[v][u] = 1;
    }


    int ans = 0;
    for(int u = 0; u < N; ++u){
        if(s[u] == '('){
            ans += dfs(u,1,1,0);
            ans %= MOD;
        }
        else if(s[u] == '-'){
            ans += dfs(u,1,0,0);
            ans %= MOD;
        }
        else if(is_digit(s[u])){
            ans += dfs(u,1,0,s[u]=='0');
            ans %= MOD;    
        }
    }
    cout << ans << endl;
    return 0;
}

大家6.1开心不,开心的留个言。

886

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值