P1073 [NOIP2009 提高组] 最优贸易

79 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

题意:给出n个点和n个点的出售价格,m条边,可能是单向边也可能是双向边,可以从u点买到东西再到v点卖出,这个活动只能做一次,问可以转的最多的钱是多少 思路:一开始先用vector把图存起来,然后从1号点开始dfs,dfs的参数有(int x,int minn,int pre),分别表示当前的点,到现在为止的最小值,上一个点;函数里需要做的事情就是把最小值更新,即minn=min(c[x],minn),还有更新答案,即f[x]=max(f[pre],c[x]-minn),这个就和动态规划差不多的思想,为了程序不发生mle,需要剪一下枝,如果最小值没更新而且答案也没有更新的话就直接返回就行,因为这个点以前已经被更新过了,而且这一次也没有再更新,说明再往后的点还是和以前一样,所以直接退出就行,类似于vis数组的思想 代码:

#include<bits/stdc++.h>
//#pragma-GCC-optimize("-Ofast");
#define ll long long
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const ll mod=998244353;
const ll inf=1e18;
const double pi=acos(-1);
const int N=1e6+100;
ll qpow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a){return qpow(a,mod-2);}
int pri[100005],isp[100005],cnt;
void ispri(int n){
    memset(isp,1,sizeof(isp));
    isp[0]=isp[1]=0;
    for(int i=2;i<=n;i++){
        if(isp[i]) pri[++cnt]=i;
        for(int j=1;j<=cnt&&pri[j]*i<=n;j++){
            isp[pri[j]*i]=0;
            if(i%pri[j]==0) break;
        }
    }
}
int n,m,c[100005],mi[1000005],f[100005];
vector<int>g[100005];
void dfs(int x,int minn,int p){
    int flag=1;
    minn=min(c[x],minn);
    if(mi[x]>minn) mi[x]=minn,flag=0;
    int maxx=max(f[p],c[x]-minn);
    if(f[x]<maxx) f[x]=maxx,flag=0;
    if(flag) return;
    for(auto u:g[x]) dfs(u,minn,x);

}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>c[i],mi[i]=inf;
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        g[u].push_back(v);
        if(w==2) g[v].push_back(u);
    }
    dfs(1,inf,0);
    cout<<f[n]<<endl;
    return 0;
}