思路:
一、直接建图跑最小费用最大流,现场比的时候被卡了SPFA了。SPFA 要吧队列改成栈才能过,具体构图如下:每个点拆成2个点(1,-1) (前面表示流量,后面表示费用),然后吃完a能再吃b的话就连 一条a->b(1,0),每个点连向汇点(1,0),源点流向一个中间点 ss (2,0) 表示2个人走,ss流向每个人(1,0)。
二、建图图上进行dp ,dp[i][j]表示表示两个人分别在 第i个点和第j个点时候能吃的最多苹果树,可以限制下 i<=j减少有效状态,还有就是一些无用的边不需要构造出来 ,比如说
a->b b->c 那么 就不需要连a->c的边,这里边表示的是 吃完a可以吃b。然后就图上跑一遍dp就行了。注意事项见代码。
第一种思路代码:
#include<queue>
#include<limits>
#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf = ~0U>>2;
const int N = 2005 , M = 2000005;
typedef pair<int,int> pii;
struct MCMF{
int h[N] , dis[N] , ing[N] , pre[N] , s , t , n;
int to[M] , ne[M] , cap[M] , cost[M] , e;
void ini(){
fill(h,h+N,-1);
e = 0;
}
void liu(int u,int v,int c,int w){
to[e] = v , ne[e] = h[u] , cap[e] = c , cost[e] = w;
h[u] = e++;
}
void link(int u,int v,int c,int w){
liu(u,v,c,w);
liu(v,u,0,-w);
}
int st[N];
bool spfa(){
// queue<int> Q;
int top=0;
fill(ing,ing+n,0);
fill(pre,pre+n,-1);
fill(dis,dis+n,inf);
ing[s] = true , dis[s] = 0;
st[top++]=s;
while(top>0){
top--;
int c=st[top];
// int c = Q.front();Q.pop();
ing[c] = false;
for(int k=h[c];~k;k=ne[k]){
int v = to[k];
if(cap[k] <= 0) continue;
if(dis[c] + cost[k] < dis[v]){
dis[v] = dis[c] + cost[k];
pre[v] = k;
if(!ing[v]) {
st[top++]=v;
ing[v]=true;
// Q.push(v) , ing[v] = true;
}
}
}
}
return dis[t] != inf;
}
int flow , mincost;
pii run(int _s,int _t,int _n){
s = _s , t = _t , n = _n;
flow = mincost = 0;
while(spfa()){
int pl = inf , p , k;
for(p=t;p!=s;p=to[k^1]){
k = pre[p];
pl = min(pl,cap[k]);
}
for(p=t;p!=s;p=to[k^1]){
k = pre[p];
cap[k] -= pl;
cap[k^1] += pl;
}
mincost += pl * dis[t];
flow += pl;
}
return make_pair(flow,mincost);
}
};
struct g{
int h,v;
}a[2000];
bool cmp(g a,g b)
{
if (a.h==b.h) return a.v<b.v;
return a.h>b.h;
}
int n,i,j,s,t,ss;
const int MAXN = 3000;
MCMF mcmf;
int main()
{
int test;
scanf("%d",&test);
while (test--)
{
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d%d",&a[i].h,&a[i].v);
sort(a+1,a+1+n,cmp);
s=0;t=2*n+2;ss=2*n+1;
mcmf.ini();
mcmf.link(s,ss,2,0);
for (i=1;i<=n;i++)
{
mcmf.link(ss,i,1,0);
mcmf.link(i,n+i,1,-1);
mcmf.link(n+i,t,1,0);
}
for (i=1;i<=n;i++)
for (j=i+1;j<=n;j++)
if (a[i].v<=a[j].v)
mcmf.link(n+i,j,1,0);
pair<int,int> ans=mcmf.run(s,t,2*n+3);
printf("%d\n",-ans.second);
}
}
第二种思路代码:
#include <queue>
#include <limits>
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
#define INF 1000000050
#define MAXN 1111
vector<int> e[MAXN];
int in[MAXN];
int q[MAXN];
int dis[MAXN];
int d[MAXN][MAXN];
int id[MAXN];
struct g {
int h, d;
} s[MAXN];
bool cmp(g a, g b) {
if (a.h == b.h)
return a.d < b.d;
return a.h > b.h;
}
int main() {
int tt;
scanf("%d", &tt);
while (tt--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d", &s[i].h, &s[i].d);
sort(s + 1, s + 1 + n, cmp);
for (int i = 0; i <= n + 1; ++i) {
dis[i] = in[i] = 0;
e[i].clear();
}
//进行拓扑排序,根据拓扑序进行dp
for (int i = 1; i <= n; ++i) {
e[0].push_back(i);
in[i]++;
for (int j = i + 1; j <= n; ++j) {
if (s[i].d <= s[j].d) {
e[i].push_back(j);
in[j]++;
}
}
e[i].push_back(n + 1);
in[n + 1]++;
}
int tail = 0;
q[tail++] = 0;
dis[0] = 0;
id[0] = 0;
for (int i = 0; i < tail; ++i) {
int u = q[i];
id[u] = i;
for (int j = 0; j < e[u].size(); ++j) {
int v = e[u][j];
if (dis[v] < dis[u] + 1)
dis[v] = dis[u] + 1;
in[v]--;
if (in[v] == 0)
q[tail++] = v;
}
}
for (int i = 0; i <= n + 1; ++i)
for (int j = 0; j <= n + 1; ++j) {
d[i][j] = -INF;
}
for (int i = 0; i <= n + 1; ++i)
e[i].clear();
s[n + 1].d = INF;
for (int i = n; i >= 0; --i) {//加边的时候注意不要加一些无用的边
int mm = INF + 50;
for (int j = i + 1; j <= n + 1; ++j) {
if (mm <= s[j].d)
continue;
if (s[i].d <= s[j].d) {
e[i].push_back(j);
mm = min(s[j].d, mm);
}
}
}
d[0][0] = 0;
for (int i = 0; i <= n + 1; ++i) {
int u = q[i];
for (int j = i; j <= n + 1; ++j) {
int v = q[j];
if (d[i][j] < 0)
continue;
for (int k = 0; k < e[u].size(); ++k) {//每次只从编号小的转移
int x = id[e[u][k]];
if (x == j) {
d[x][j] = max(d[x][j], d[i][j]);
}
if (x < j) {
d[x][j] = max(d[x][j], d[i][j] + 1);
}
if (x > j) {
d[j][x] = max(d[j][x], d[i][j] + 1);
}
}
}
}
printf("%d\n", d[n + 1][n + 1] - 1);
}
}