HDU 5406 CRB and Apple (2015 Multi-University Training Contest 10)

思路:

一、直接建图跑最小费用最大流,现场比的时候被卡了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);
	}
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值