2014年西安区域赛C The Problem Needs 3D Arrays

题意:给定一个1-n的排列,它的一个子序列(不一定连续)设为S,令这个子序列的逆序对为R(S),长度为L(S)。请找到一个S,使得 R(S)L(S) 最大。
题解:两个数字如果有逆序关系则连边,跑一遍最大密度子图就是答案。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define snuke(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
#define eps 1e-10
typedef double F;
#define F_INF (1e20)
#define MAXV 10000
#define MAXE 80*10000// E*2!
F cap[MAXE],flow[MAXE];
int to[MAXE],_prev[MAXE],last[MAXV],used[MAXV],level[MAXV];
struct MaxFlow{
    int V,E;
    MaxFlow(int n){
        int i;
        V=n;E=0;
        REP(i,V) last[i]=-1;
    }
    void add_edge(int x,int y,F f){
        cap[E]=f;flow[E]=0;to[E]=y;_prev[E]=last[x];last[x]=E;E++;
        cap[E]=0;flow[E]=0;to[E]=x;_prev[E]=last[y];last[y]=E;E++;
    }
    bool bfs(int s,int t){
        int i;
        REP(i,V) level[i]=-1;
        queue<int> q;
        q.push(s);level[s]=0;
        while(!q.empty()){
            int x=q.front();q.pop();
            for(i=last[x];i>=0;i=_prev[i]) if(level[to[i]]==-1&&cap[i]>flow[i]) {q.push(to[i]);level[to[i]]=level[x] + 1;}
        }
        return (level[t]!=-1);
    }
    F dfs(int v,int t,F f){
        int i;
        if(v==t) return f;
        for(i=used[v];i>=0;used[v]=i=_prev[i]) if(level[to[i]]>level[v]&&cap[i]>flow[i]){
            F tmp=dfs(to[i],t,min(f,cap[i]-flow[i]));
            if(tmp>0) {flow[i]+=tmp;flow[i^1]-=tmp;return tmp;}
        }
        return 0;
    }
    F maxflow(int s,int t){
        int i;
        while(bfs(s,t)){
            REP(i,V) used[i]=last[i];
            while(dfs(s,t,F_INF) !=0);
        }
        F ans=0;
        for(i=last[s];i>=0;i=_prev[i]) ans+=flow[i];
        return ans;
    }
};
int n,m;
template<typename __ll>
inline void read(__ll &m)
{
    __ll x=0,f=1;char ch=getchar();
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    m=x*f;
}
struct edg{
    int u,v;
}E[MAXE];
bool check(double k){
    int i;
    MaxFlow f(m+n+2);
    for(i=1;i<=n;i++) f.add_edge(i,m+n+1,k);
    for(i=1;i<=m;i++) f.add_edge(n+i,E[i].u,F_INF),f.add_edge(n+i,E[i].v,F_INF),f.add_edge(0,n+i,1);
    double ans=m-f.maxflow(0,m+n+1);
    return ans<eps;
}
int a[111];
int main()
{
    int i,j,u,v,T,cas=1;
    cin>>T;
    while(T--){
        cin>>n;
        m=0;
        for(i=1;i<=n;i++) read(a[i]);
        for(i=1;i<=n;i++)
            for(j=i+1;j<=n;j++)
                if(a[i]>a[j])
                    E[++m].u=i,E[m].v=j;
        double l=0,r=m,mid;
        while(l+eps<=r){
            mid=(l+r)/2;
            if(check(mid)) r=mid;
            else l=mid;
        }
        printf("Case #%d: %.10lf\n",cas++,l);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值