感觉这是一道很不错的dp呢,比赛的时候想了许久也没做出来,然后一开始过了七八十人后来好多人都被hack(因为都是用暴力水),最后只过了22个,所以可见这道题有较大的思维难度额。
上题:在Byteland一共有n个城市,编号依次为1到n,同时有m条单向道路连接着这些城市,其中第ii条道路的起点为ui,终点为vi(这里有个关键条件,就是每条路都是起点比终点小,这样就不会出现环了,而且后面也不需要用toposort了)
特工团队一共有33名成员:007,008,以及009,他们将要执行qq次秘密任务。
在每次任务中,三人可能会处于三个不同的城市,他们互相之间通过对讲机保持联络。编号为ii的城市的无线电频为wi,如果两个城市的无线电频差值的绝对值不超过K,那么无线电就可以接通。三个特工每个时刻必须要选择一条道路,走到下一个城市,每条道路都只需要花费1单位时间。
他们可以选择在任意城市终止任务,甚至可以在起点就终止任务,但不允许在道路上终止任务。现在他们想知道,对于每次任务,给定三个人的起始位置,有多少种可能的合法行动方案,使得行动过程中任意在城市的时刻,他们都可以两两联络?
两个方案被视作不同当且仅当至少存在一个人在某一时刻所在的城市不同。
注意:33个特工必须同时结束任务。
一开始的思路:很容易就想到了n^6的暴力再toposort,显然不行,悲伤。后来看了clarification,发现了那个很重要的条件,但还是要n^6,直到比赛结束也没想出来。
题解:多开一维,dp[i][j][k][l],最后一维纪录该谁走,0是007走,1是008走,2是009走,这样转移的时候就需要在009走完,转移到007走的时候判断一下这三个点是否可以通信就可以了,复杂度大大降低(主要是设置了一个序,来使得操作有序,这样三个嵌套的循环就变成并列的了,复杂度也变成n^4了)。详细见代码。
//
// main.cpp
// D
//
// Created by 黄宇凡 on 8/6/16.
// Copyright © 2016 黄宇凡. All rights reserved.
//
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
const int maxn = 55;
const int mod = 998244353;
int dp[maxn][maxn][maxn][4];
vector<int> G[maxn];
int w[maxn];
int n;int T;
int m,q,K;
int main(int argc, const char * argv[]) {
cin >> T;
while(T--){
cin >> n >> m >> K >> q;
for(int i = 1;i <= n;i++) scanf("%d",w + i),G[i].clear();
for(int i = 1;i <= m;i++){
int u,v;
scanf("%d%d",&u,&v);
G[v].push_back(u);
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
for(int k = 1;k <= n;k++){
for(int l = 0;l < 4;l++) dp[i][j][k][l] = 0;
if(abs(w[i] - w[j]) <= K && abs(w[i] - w[k]) <= K && abs(w[j] - w[k]) <= K) dp[i][j][k][0] = 1;
}
}
}
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[i].size();l++){
int v = G[i][l];
(dp[v][j][k][1] += dp[i][j][k][0]) %= mod;
}
for(int l = 0;l < G[j].size();l++){
int v = G[j][l];
(dp[i][v][k][2] += dp[i][j][k][1]) %= mod;
}
for(int l = 0;l < G[k].size();l++){
int v = G[k][l];
if(abs(w[i] - w[j]) <= K && abs(w[v] - w[i]) <= K && abs(w[v] - w[j]) <= K)
(dp[i][j][v][0] += dp[i][j][k][2]) %= mod;
}
}
}
}
while(q--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",dp[x][y][z][0]);
}
}
return 0;
}
很巧妙的一个dp,给人以启发。