假期计划-暴力出奇迹
该题解为另类做法,正解请移步其他题解
家 → 景点 A → 景点 B → 景点 C →景点 D → 家,求途径点权之和的最大值。
看到这样一道题目,很容易能够想到使用朴素的
然后再认真一看:“去
不过我们仔细一想,因为经过的景点数很少,其实可以
话不多说,思路简单但码量巨大的代码来咯
#include<bits/stdc++.h>
#define x first
#define y second
#define pf push_front
#define pb push_back
#define rep(i,a,b) for(int i=a;i<(int)(b);i++)
#define r0p(i,n) rep(i,0,n)
#define r1p(i,n) rep(i,1,n+1)
#define all(a) a.begin(),a.end()
#define dbg(a) cerr<<#a<<": "<<a<<endl
#define sz(a) (int)(a.size())
#define re(a) cout<<a<<endl,exit(0)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int intmx=0x3f3f3f3f;
const ll llmx=LLONG_MAX-10000ll*INT_MAX;
template <typename T> inline T abs(T a){return a<0?-a:a;}
void IO(string s=""){
if(!sz(s)) return;
freopen((s+".in").c_str(),"r",stdin);
freopen((s+".out").c_str(),"w",stdout);
}
#define N 2505
#define M 10005
#define L 10//此处最好开大一点,防止别卡。(虽然实测开成4就能过)
int n,m,k;
ll v[N]={0};
ll dp[N][6][L][6]={0};//dp[i][e][l]表示走e步到i后点权和第l大的路径方案,dp[i][e][l][0]表示路径方案的点权和,dp[i][e][l][tmp](tmp>0)表示该路径方案途径的第tmp个点
vector<int>b[N];
vector<int>a[N];
bool ok[N][N]={0};
queue<pair<int,int> >q;
inline void upd(int i,int e,int lev){
if(lev!=-1&&dp[i][e][lev][0]<dp[i][e][lev+1][0]){
for(int tmp=0;tmp<=e;tmp++) swap(dp[i][e][lev+1][tmp],dp[i][e][lev][tmp]);
upd(i,e,lev-1);
}
}//更新后将dp[i][e]手动冒泡排序
inline bool conf(int i,int j,int e,int l){
for(int tmp=1;tmp<=e;tmp++) if(j==dp[i][e][l][tmp]) return 1;
return 0;
}//即将经过的点与之前的点不冲突
signed main(){
scanf("%d%d%d",&n,&m,&k);
rep(i,2,n+1) scanf("%lld",&v[i]);
r0p(i,m){
int u,vv;
scanf("%d%d",&u,&vv);
b[u].pb(vv);
b[vv].pb(u);
}
r1p(i,n){
//建图
q.push({i,-1});
ok[i][i]=1;
while(!q.empty()){
int j=q.front().x,val=q.front().y;
q.pop();
if(val==k) continue;
for(auto l:b[j]) if(!ok[i][l]){
ok[i][l]=1;
a[i].pb(l);
q.push({l,val+1});
}
}
//初始化dp值
r0p(e,6) r0p(l,L) dp[i][e][l][0]=-1;
}
dp[1][0][0][0]=0;
r0p(e,5){//刷表法
r1p(i,n) r0p(l,L) if(dp[i][e][l][0]>=0){//该路径方案存在
for(auto j:a[i]) if(e==4?j==1:!conf(i,j,e,l)&&dp[j][e+1][L-1][0]<dp[i][e][l][0]+v[j]){//不与之前的点冲突且点权和大于当前倒一(值得被更新)
//更新倒一
dp[j][e+1][L-1][0]=dp[i][e][l][0]+v[j];
for(int tmp=1;tmp<=e;tmp++) dp[j][e+1][L-1][tmp]=dp[i][e][l][tmp];
dp[j][e+1][L-1][e+1]=j;
//向前冒泡
upd(j,e+1,L-2);
}
}
}
printf("%lld\n",dp[1][5][0][0]);
exit(0);
}