zoukankan      html  css  js  c++  java
  • 机房测试10:a 礼物分配(差分约束 or 贪心+线段树)

    题目:

     

     分析:

    法一: 差分约束

    将题目中的限制写成三个不等式:(s数组是前缀和)

    1.  s[r]-s[l-1]>=c

    2.  s[r]-s[r-1]>=0

    3.  s[r]-s[r-1]<=1

    将第一个式子移项成最短路中dis的形式:s[r]>=s[l-1]+c

    这个式子与:dis[i] >= dis[j]+c 相似,也就是说,j到i有一条c的边,j可以去更新i,所以将l-1连一条r的边

    下面两个式子也类似,但不是跑最短路,是跑最长路!!!

    如果要跑最短路,就应该反着连负边。(画图模拟一下会发现)

    下面的代码跑的是最短路,//划掉的是跑最长路的连边

    最后spfa注意出队要标记置0!!

    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int
    #define N 1005
    #define M 100005
    int read()
    {
        int x=0,fl=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') fl=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
        return x*fl;
    }
    int n,m,fl[N],dis[N],w[M],head[N],nex[M],to[M],tot=0;
    int l[N],r[N],c[N],sum[N];
    void add(int a,int b,int c) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=c; }
    queue<int> q;
    void spfa()
    {
        //memset(dis,-10,sizeof(dis));
        //dis[0]=0; q.push(0); fl[0]=1;
        memset(dis,0x3f3f3f,sizeof(dis));
        dis[n]=0; q.push(n); fl[n]=1;
        while(!q.empty()){
            int u=q.front(); q.pop();
            fl[u]=0;
            for(ri i=head[u];i;i=nex[i]){
                int v=to[i];
                if(dis[v]>dis[u]+w[i]){
                    dis[v]=dis[u]+w[i];
                    if(!fl[v]) q.push(v);
                }
            }
        }
        printf("%d
    ",-dis[0]);
    }
    int ans=1<<20;
    void dfs(int now)
    {
        if(now>n){
            bool fl=0;
            for(ri i=1;i<=n;++i) sum[i]=sum[i-1]+w[i];
            for(ri i=1;i<=m;++i) if(sum[r[i]]-sum[l[i]-1]<c[i]) { fl=1; break; }
            if(!fl) ans=min(ans,sum[n]);
            return ;
        }
        w[now]=0;
        dfs(now+1);
        w[now]=1;
        dfs(now+1);
    }
    void work1()
    {
        for(ri i=1;i<=m;++i) l[i]=read(),r[i]=read(),c[i]=read();
        dfs(1);
        printf("%d
    ",ans);
    }
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        n=read(); m=read(); 
        if(n<=10 && m<=10) { work1(); return 0; }
        int a,b,cc;
        for(ri i=1;i<=m;++i)
        a=read(),b=read(),cc=read(),add(b,a-1,-cc);//add(a-1,b,c)
        for(ri i=1;i<=n;++i)
        //add(i-1,i,0),add(i,i-1,-1);
        add(i,i-1,0),add(i-1,i,1);
        spfa();
        return 0;
    }
    /*
    15 5
    3 7 3
    8 10 3
    6 8 1
    1 3 1
    10 11 1
    
    4 3
    1 3 1
    2 2 1
    3 4 2
    */
    View Code

    法二:贪心+线段树

    将区间排序(按左端点),每一次尽量贪心地将区间里面的元素往后放,(这样会使最后放的尽量少)。

    在处理一个区间的时候,先看这个区间中已经有多少已经被选了,如果够了限制,就跳过,否则在区间末尾选够。

    用线段树维护区间修改与区间查询。

  • 相关阅读:
    SQL的join使用图解
    归并排序的JAVA实现
    java 快速排序 时间复杂度 空间复杂度 稳定性
    哈希表(HashMap)分析及实现(JAVA)
    外部排序
    海量数据面试题整理
    《CSS3秘籍》第6、7章
    《CSS3秘籍》第3-5章
    《CSS3秘籍》第1、2章
    《HTML5与CSS3基础教程》第11、14-16、18章
  • 原文地址:https://www.cnblogs.com/mowanying/p/11645845.html
Copyright © 2011-2022 走看看