HDU 5807 Keep In Touch(dp)

来源:互联网 发布:红楼梦 知乎 编辑:程序博客网 时间:2024/06/05 11:15

Description
在Byteland一共有n个城市,编号依次为1到n,同时有m条单向道路连接着这些城市,其中第ii条道路的起点为ui,终点为vi(1<=ui<=vi<=n),特工团队一共有3名成员:007,008,以及009,他们将要执行q次秘密任务。
在每次任务中,三人可能会处于三个不同的城市,他们互相之间通过对讲机保持联络。编号为i的城市的无线电频为wi,如果两个城市的无线电频差值的绝对值不超过K,那么无线电就可以接通。三个特工每个时刻必须要选择一条道路,走到下一个城市,每条道路都只需要花费1单位时间。
他们可以选择在任意城市终止任务,甚至可以在起点就终止任务,但不允许在道路上终止任务。现在他们想知道,对于每次任务,给定三个人的起始位置,有多少种可能的合法行动方案,使得行动过程中任意在城市的时刻,他们都可以两两联络?
两个方案被视作不同当且仅当至少存在一个人在某一时刻所在的城市不同。
注意:3个特工必须同时结束任务。
Input
输入的第一行包含一个正整数T(1≤T≤10),表示测试数据的组数。
对于每组数据,第一行包含四个整数n(1≤n≤50),m(0≤m≤n*(n-1)/2),K(0≤K≤10^9),q(1≤q≤125000),表示城市数,道路数,允许的差值上限以及任务个数。
第二行包含n个正整数wi(1<=wi<=10^9),依次表示每个城市的无线电频。
接下来m行,每行包含两个正整数ui,vi(1<=ui<=vi<=n),表示一条单向道路,数据保证没有重边。
接下来q行,每行包含三个正整数x,y,z(1≤x,y,z≤n),表示一次任务中三个人的起始位置。数据保证在起始位置三个人可以两两联络。
Output
对于每组数据,输出q行,对于每个任务输出方案数。由于答案可能很大,请对998244353取模。
Sample Input
1
4 4 2 10
8 8 4 1
1 3
1 4
2 3
2 4
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
3 3 3
4 4 4
Sample Output
3
3
3
3
3
3
3
3
1
1
Solution
直接枚举当前三个人的状态以及下一步状态时间复杂度O(n^6),考虑优化,本来是三个人同时走向下一个城市,现在给这三个同步的操作定一个顺序,那么每个合法任务应该是007,008,009,007,008,009,……,007,008,009
dp[i][j][k][0]表示当前三人分别在i,j,k城市,且下一步该i走的方法数;
dp[i][j][k][1]表示当前三人分别在i,j,k城市,且下一步该j走的方法数;
dp[i][j][k][2]表示当前三人分别在i,j,k城市,且下一步该k走的方法数;
那么dp[i][j][k][0]的后继状态就是dp[ii][j][k][1](ii为i的出边对应的另一端点),dp[i][j][k][1]的后继状态就是dp[i][jj][k][2](jj为j的出边对应的另一端点),dp[i][j][k][2]的后继状态就是dp[i][j][kk][0](kk为k的出边对应的另一端点),进而我们就可以由dp[0]->dp[1],dp[1]->dp[2],dp[2]->dp[0]得到所有状态的方法数,初始化的时候如果i,j,k满足条件则令dp[i][j][k][0]=1,否则令dp[i][j][k]=0,注意最后由dp[2]->dp[0]的时候需要判断这个dp[i][j][k][0]是否满足条件,求出dp数组后对于每次查询,如果三个特工初始位置是a,b,c的话,那么答案就是dp[a][b][c][0]
Code

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<algorithm>#include<vector>using namespace std;typedef long long ll;#define mod 998244353#define maxn 55vector<int>g[maxn];int T,n,m,K,q,w[maxn];ll dp[maxn][maxn][maxn][4];int check(int i,int j,int k){    if(abs(w[i]-w[k])>K)return 0;    if(abs(w[i]-w[j])>K)return 0;    if(abs(w[j]-w[k])>K)return 0;    return 1;}int main(){    scanf("%d",&T);    while(T--)    {        scanf("%d%d%d%d",&n,&m,&K,&q);        for(int i=1;i<=n;i++)scanf("%d",&w[i]),g[i].clear();        while(m--)        {            int u,v;            scanf("%d%d",&u,&v);            g[u].push_back(v);        }        memset(dp,0,sizeof(dp));        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                for(int k=1;k<=n;k++)                    if(check(i,j,k))dp[i][j][k][0]=1;                    else dp[i][j][k][0]=0;        for(int i=n;i>=1;i--)            for(int j=n;j>=1;j--)                for(int k=n;k>=1;k--)                {                    for(int l=0;l<g[k].size();l++)                    {                        int kk=g[k][l];                        dp[i][j][k][2]+=dp[i][j][kk][0];                        dp[i][j][k][2]%=mod;                    }                    for(int l=0;l<g[j].size();l++)                    {                        int jj=g[j][l];                        dp[i][j][k][1]+=dp[i][jj][k][2];                        dp[i][j][k][1]%=mod;                    }                                       if(!dp[i][j][k][0])continue;                    for(int l=0;l<g[i].size();l++)                    {                        int ii=g[i][l];                        dp[i][j][k][0]+=dp[ii][j][k][1];                        dp[i][j][k][0]%=mod;                    }                }        while(q--)        {            int a,b,c;            scanf("%d%d%d",&a,&b,&c);            printf("%I64d\n",dp[a][b][c][0]);        }    }    return 0;}
0 0
原创粉丝点击