題意:給定一個長度爲\(n\)的數字序列,現能夠從中選取k個單調上升的子序列,且每一個元素至多隻能被選中一次,問k個子序列元素和的最大值。php
題解:對數列每一項拆點,鏈接流量爲1,費用爲\(-a[i]\)的邊,全部項的右端點與其後大於等於這一項的項的左端點連流量1,費用0的邊。源點與匯點再分別與各項左右端點連邊。超級源點與源點連流量k費用0的邊。跑最小費用最大流。node
#include <bits/stdc++.h> using namespace std; int n,k,a[2005]; typedef pair<int,int> pa; struct node{ int to,flo,cost,rev; }; int ss,s,t,tt; vector<node> gra[5000]; void add(int u,int v,int flo,int cost){ node tmp; tmp.to=v; tmp.flo=flo; tmp.cost=cost; tmp.rev=gra[v].size(); gra[u].push_back(tmp); tmp.to=u; tmp.flo=0; tmp.cost=-cost; tmp.rev=gra[u].size()-1; gra[v].push_back(tmp); } int h[5000],dis[5000]; int pre[5000],ppre[5000]; bool dij(){ for(int i=ss;i<=tt;i++) dis[i]=0x3f3f3f3f; priority_queue<pa,vector<pa>,greater<pa> > que; que.push(make_pair(0,ss)); dis[ss]=0; while(!que.empty()){ pa now=que.top(); que.pop(); if(dis[now.second]<now.first) continue; int u=now.second; int siz=gra[u].size(); for(int i=0;i<siz;i++){ node ttt=gra[u][i]; int tmp=ttt.cost+h[u]-h[ttt.to]; if(ttt.flo>0&&dis[ttt.to]>dis[u]+tmp){ dis[ttt.to]=dis[u]+tmp; pre[ttt.to]=u; ppre[ttt.to]=i; que.push(make_pair(dis[ttt.to],ttt.to)); } } } return dis[tt]!=0x3f3f3f3f; } int solve(){ int ans=0; while(dij()){ for(int i=ss;i<=tt;i++) h[i]+=dis[i]; ans -= h[tt]; int now=tt; while(now!=ss){ int nxt=pre[now],id=ppre[now]; gra[nxt][id].flo--; gra[gra[nxt][id].to][gra[nxt][id].rev].flo++; now=nxt; } } return ans; } int main(){ int T; cin >> T; while(T--){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); ss=0; s=1; t=n*2+2; tt=n*2+3; for(int i=ss;i<=tt;i++) gra[i].clear(); add(0,1,k,0); add(t,tt,k,0); for(int i=1;i<=n;i++){ int u=i*2,v=i*2+1; add(1,u,1,0); add(u,v,1,-a[i]); add(v,t,1,0); for(int j=i+1;j<=n;j++){ if(a[j]>=a[i]){ add(v,j*2,1,0); } } } cout << solve() << endl; } return 0; }