题目链接
题目思路
这个题意有点难说清,还是自己读比较好
这个题目我觉得是比较套路的,首先只有\(2*k+2\)个点有用
一个起点加上终点再加上梯子两边的起始点
问题是如何建边是一个问题,暴力建边显然会达到\(n^2\)的
主要是在每一行内可以进行\(dp\)转移,一次从左往右,一次从右往左,一个有点意思的\(dp\)
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=2e6+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,m,k;
int cnt;
vector<pii> g[maxn];
pii to[maxn];
ll x[maxn];
ll dp[maxn];
void init(){
for(int i=1;i<=cnt;i++){
to[i]={0,0};
}
cnt=0;
for(int i=1;i<=n;i++){
g[i].clear();
}
}
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%d",&x[i]);
}
init();
g[1].push_back({1,++cnt});
for(int i=1,a,b,c,d,h;i<=k;i++){
scanf("%d%d%d%d%d",&a,&b,&c,&d,&h);
g[a].push_back({b,++cnt});
g[c].push_back({d,++cnt});
to[cnt-1]={cnt,-h};// 建边
}
g[n].push_back({m,++cnt});
for(int i=1;i<=cnt;i++){
dp[i]=INF;
}
dp[1]=0;
for(int i=1;i<=n;i++){
sort(g[i].begin(),g[i].end());
int sz=g[i].size();
for(int j=1;j<sz;j++){
dp[g[i][j].se]=min(dp[g[i][j].se],dp[g[i][j-1].se]+x[i]*(g[i][j].fi-g[i][j-1].fi));
}
for(int j=sz-2;j>=0;j--){
dp[g[i][j].se]=min(dp[g[i][j].se],dp[g[i][j+1].se]+x[i]*(g[i][j+1].fi-g[i][j].fi));
}
for(int j=0;j<sz;j++){
dp[to[g[i][j].se].fi]=min(dp[to[g[i][j].se].fi],dp[g[i][j].se]+to[g[i][j].se].se);
}
}
if(dp[cnt]>=1e18){
printf("NO ESCAPE\n");
}else{
printf("%lld\n",dp[cnt]);
}
}
return 0;
}
// 3 1 3 2