摘自:http://blog.csdn.net/enjoy_pascal/article/details/78592786 写的很简短
analysis
有人说这是防AK题?不算吧……(只不过我并没有做出来顶乱用)
首先用邻接表存边跑一遍spfa,dis[i]表示1到i的最短路长度(不要和我说你不会spfa)
设f[i][j]表示从1到i的所有路径里,比dis[n]大K的路径条数
所以有
看到这个,你以为是DP?
记忆化搜索!
我们从n开始倒着搜索,若dis[i]−dis[k]+j−len[i][k]<0当然也就不搜索了
那么我们最大的敌人——判0环呢?
若f[x][y]这个状态在一遍dfs里出现两次,那就是有0环,return就好,开个数组标记一下
finally
他的代码。我在写。。。。。。已经通过了7组数据 ,0环还在研究
/*输入
1
5 10 0 595078320
1 2 1
2 5 1
2 1 2
5 2 1
2 3 1
3 5 1
1 5 2
4 1 1
3 1 2
4 2 1
输出 2
我的输出:4 未通过*/
#include <bits/stdc++.h>
using namespace std;
const int MAX_N=100001;
struct edge{
int to;
int cost;
};
vector<edge> G[MAX_N];
vector<edge> ReG[MAX_N];
int dis[MAX_N];
int N,M,K,pmod;
int dp[MAX_N][51];
bool roll;
void input(){
for(int i=0;i<MAX_N;i++)
{
G[i].clear();
ReG[i].clear();
}
cin >> N >> M >> K >> pmod;
for(int i=1;i<=M;i++){
int a,b,c; cin >> a >> b>>c;
G[a].push_back( (edge){b,c} );
ReG[b].push_back( (edge){a,c} );
}
memset(dp, 0 ,sizeof(dp));
dp[1][0]=1;
fill(dis ,dis+N+1, 200000000);
roll=false;
}
int dfs(int v,int k){
if(dp[v][k]!=0) return dp[v][k];
//used[v]=true;
for( int j=0; j<ReG[v].size(); j++ ){
edge e = ReG[v][j];
int t = k -(dis[e.to] + e.cost - dis[v] );
if(t<0) continue;
if( dp[e.to][t]!=0 ) roll=true;
dp[v][k] = ( dp[v][k]+ dfs( e.to, t)%pmod ) %pmod;
}
return dp[v][k];
}
void dijkstra(){
typedef pair<int ,int> P;
priority_queue<P, vector<P>,greater<P> > que;
dis[1] = 0;
que.push( P(0,1));
while( !que.empty()){
P p = que.top(); que.pop();
int v= p.second;
if( dis[v] < p.first ) continue;
for( int i=0 ; i<G[v].size(); i++){
edge e =G[v][i] ;
if( dis[e.to] > dis[v] + e.cost ){
dis[e.to] = dis[v] + e.cost;
que.push( P( dis[e.to] , e.to ));
}
}
}
}
int main() {
//freopen("park.in","r",stdin);
//freopen("park.out","w",stdout);
int T;cin>>T;
while(T--){
input();
dijkstra();
int ans=0;
for(int i=0;i<=K; i++){
ans = ( ans + dfs(N,i) ) % pmod;
}
//if(roll) cout << -1 <<endl;
// else
cout << ans<<endl;
}
return 0;
}
NOIP 2017 逛公园 动态规划!
http://blog.csdn.net/qq_38678604/article/details/78572603#t4 这一篇写得好
https://www.cnblogs.com/CQzhangyu/p/7825839.html
https://www.cnblogs.com/Dndlns/p/7895996.html
//#define debug
#include <bits/stdc++.h>
using namespace std;
const int MAX_N=1000001;
struct edge{
int to;
int cost;
};
vector<edge> G[MAX_N];
// vector<edge> ReG[MAX_N];
long long dis[MAX_N]; //存储最短路径
int N,M,K,pmod;
// bool used[MAX_N];
//int rudu[MAX_N] ; // 存储 入度的值
int dp[MAX_N][51];
vector<edge> O[MAX_N]; //为零边建立图
vector<edge> RO[MAX_N]; //零边建立方向图
void input(){
cin >> N >> M >> K >> pmod;
for(int i=1;i<=M;i++){
int a,b,c; cin >> a >> b>>c;
G[a].push_back( (edge){b,c} );
// rudu[b]++;
//ReG[b].push_back( (edge){a,c});
if( c==0) {
O[a].push_back( (edge){b,c} ) ;
RO[b].push_back( (edge){a,c} ) ;
}
}
memset(dp, 0 ,sizeof(dp));
dp[1][0]=1;
fill(dis ,dis+N+1, 2000);
// for(int i=1;i<=M;i++){
//
// for( int j=0;j<G[i].size();j++)
// {
// cout << i <<" "<< G[i][j].to <<" "<< G[i][j].cost << " " <<endl;;
// }
// cout << endl;
// }
}
namespace roll {
bool used[MAX_N];
int cmp[MAX_N];
vector<int> vs ; //后序遍历的顶点表
bool atO[MAX_N];
void dfs(int v){
used[v]=true;
for( int j=0; j<O[v].size(); j++ ){
edge e = O[v][j];
if( !used[e.to] ) dfs( e.to);
}
vs.push_back(v);
}
void rdfs(int v ,int k){
used[v]=true;
cmp[v]=k;
for( int j=0; j<RO[v].size(); j++ ){
edge e = RO[v][j];
if( !used[e.to] ) rdfs( e.to, k);
}
}
//强联通分量
void scc(){
memset(used, false , sizeof(used) );
//memset(cmp ,-1 ,sizeof(cmp));
fill(cmp,cmp+N+1,-1);
memset(atO, 0,sizeof(atO));
vs.clear();
for(int i=1;i<=N;i++) {
if( !used[i]) dfs(i);
}
#ifdef debug
cout << endl << "VS::";
for(int i=1; i<=N ;i++)
cout << vs[i] << " " ;
#endif
memset(used, false , sizeof(used) );
int t=0;
for(int i=vs.size()-1; i>=0 ;i--){
//for(int i=0; i<vs.size() ;i--){
if( !used[ vs[i]] ) rdfs( vs[i], t++);
}
#ifdef debug
cout << endl << "cmp ::"<<endl;
for(int i=1; i<=N ;i++)
cout << i << " :" <<cmp[i] << " "<<endl;
cout<<endl;
#endif
for(int i=1;i<N;i++){
// cout << i;
// cout << cmp[i];
// cout << cmp[i+1];
if( ( cmp[i]!=-1 ) && ( cmp[i+1]!=-1 ) && ( cmp[i] == cmp[i+1]) ) { //泪: !cmp[i]==-1
atO[ i ] = true;
atO[ i+1 ] = true;
}
}
#ifdef debug
cout << endl << "atO ::";
for(int i=1; i<=N ;i++)
cout << i << ": "<< atO[i] << " ";
cout<<endl;
#endif
}
}
//从一号节点Dijkstra
void dijkstra(){
typedef pair<int ,int> P;
priority_queue<P, vector<P>,greater<P> > que;
dis[1] = 0;
que.push( P(0,1));
while( !que.empty()){
P p = que.top(); que.pop();
int v= p.second;
if( dis[v] < p.first ) continue;
for( int i=0 ; i<G[v].size(); i++){
edge e =G[v][i] ;
if( dis[e.to] > dis[v] + e.cost ){
dis[e.to] = dis[v] + e.cost;
que.push( P( dis[e.to] , e.to ));
}
}
}
#ifdef debug
cout<<endl;
cout << "dist[]: ";
for(int i=1 ;i<=N; i++){
cout<< i << " : "<< dis[i] << " ; ";
cout << endl;
}
#endif // debug
}
//为什么不用宽度搜索呢??
int BFS( ){
bool us[MAX_N];
memset( us, false ,sizeof(us));
queue<int> qq;
qq.push(1);
us[1]=true;
while(!qq.empty()){
int s=qq.front(); qq.pop();
if( roll::atO[s] ) { return -1; }
for( int j=0; j < G[s].size(); j++){
edge e = G[s][j];
if( e.cost + dis[s] - dis[e.to] > K || e.cost + dis[s] - dis[e.to]<0 ) continue;//泪!!
else
if( !us[e.to]) { qq.push(e.to) ; us[e.to] =true; };
for(int k=0; k<=K; k++){
int kk = k + e.cost + dis[s] - dis[e.to] ;
if( (kk>=0) && (kk <= K) ){
dp[e.to][ kk ] = ( dp[e.to][ kk ] + dp[s ][ k ] );
#ifdef debug
cout << "from " << s << "dp[" << e.to << "]"
<< "[" << kk << "] :"
<< dp[e.to][ kk] << endl;
#endif // debug
}
}
}
}
return 1;
}
int main()
{
//freopen("park.in","r",stdin);
//freopen("park.out","w",stdout);
int t;cin>>t;
while(t--){
input();
dijkstra();
roll::scc();
if( BFS()==-1 ) cout << -1;
else{
int ans = 0;
for(int i=0;i<=K; i++){
// cout <<"dp[N][i]" << dp[N][i];
ans = ( ans + dp[N][i ] ) % pmod;
}
cout << ans<<endl;
}
}
return 0;
}