太空飞行计划问题

题目描述:

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。

现已确定了一个可供选择的实验集合E= {E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。

实验Ej需要用到的仪器是I的子集RjÍI。配置仪器 Ik的费用为ck美元。

实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而 配置哪些仪器才能使太空飞行的净收益最大。

这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。

题解:

将问题抽象成一个图:(S源点,T汇点)

1.S->所有实验,边权为实验收入;

2.所有器材->T,边权为器材花费;

3.实验->器材,边权inf。

然后(所有收入-最大流)就是最终答案。

原因?

最大流=最小割。

(割掉==买)

首先求出的最小割一定不包含inf的边……

然后,

比如割在(器材->T)的边上,那么相当于相关实验的收入可以覆盖这个器材的花费。可以割掉。

在比如说割在(S->实验)的边上,相当于做实验的钱都花在器材身上。

真实收入+=实验,真实花费+=实验。

所以做差之后结果没变。

所以算法是正确的。

还有判断器材:

枚举删(器材->T)的边,如果删后最大流==最大流-边权,那么就用这个器材。

判断实验很简单就不说了。

具体实现:

加边,然后一直跑最大流(我用的dinic)。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 55
#define ll long long
inline int rd()
{
    int f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
    return f*c;
}
int n,m,S,T,hed[N<<1],cnt=-1;
const int inf = 0x3f3f3f3f;
struct EG
{
    int to,nxt;
    int vl;
}E[N*N*2],e[N*N*2];
void ae(int f,int t,int v)
{
    E[++cnt].to = t;
    E[cnt].nxt = hed[f];
    E[cnt].vl = v;
    hed[f] = cnt;
}
int dep[N<<1],cur[N<<1];
bool bfs(int ban)
{
    queue<int>q;
    memset(dep,0x3f,sizeof(dep));
    memcpy(cur,hed,sizeof(cur));
    dep[S]=0;
    q.push(S);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int j=hed[u];~j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(j==ban)continue;
            if(e[j].vl&&dep[to]==inf)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    if(dep[T]!=inf)return 1;
    return 0;
}
int dfs(int u,int lim,int ban)
{
    if(!lim||u==T)return lim;
    int fl=0,f;
    for(int j=cur[u];~j;j=e[j].nxt)
    {
        cur[u]=j;
        int to = e[j].to;
        if(j==ban)continue;
        if(dep[to]==dep[u]+1&&(f=dfs(to,min(lim,e[j].vl),ban)))
        {
            fl+=f;
            lim-=f;
            e[j].vl-=f;
            e[j^1].vl+=f;
            if(!lim)break;
        }
    }
    return fl;
}
int dinic(int ban)
{
    int ret = 0;
    memcpy(e,E,sizeof(e));
    while(bfs(ban))
        ret+=dfs(S,inf,ban);
    return ret;
}
bool use[N];

int main()
{
    m=rd(),n=rd();
    S=0,T=n+m+1;
    memset(hed,-1,sizeof(hed));
    int sum = 0;
    for(int x,i=1;i<=m;i++)
    {
        x=rd();
        ae(S,i,x);
        ae(i,S,0);
        sum+=x;
        char c=getchar();
        x=0;
        while(c!='\n'&&c!='\r')
        {
            if(c==' '&&x)
            {
                ae(i,x+m,inf);
                ae(x+m,i,0);
                x=0;
                c=getchar();
                continue;
            }
            x=x*10+c-'0';
            c=getchar();
        }
        if(x)
        {
            ae(i,x+m,inf);
            ae(x+m,i,0);
        }
    }
    for(int x,i=1;i<=n;i++)
    {
        x=rd();
        ae(i+m,T,x);
        ae(T,i+m,0);
    }
    int ans = dinic(-1);
    for(int i=m+1;i<=m+n;i++)
    {
        for(int j=hed[i];~j;j=E[j].nxt)
        {
            int to = E[j].to;
            if(to==T)
            {
                int tmp = dinic(j);
                if(tmp+E[j].vl==ans)use[i-m]=1;
                break;
            }
        }
    }
    for(int i=1;i<=m;i++)
    {
        bool g = 1;
        for(int j=hed[i];~j;j=E[j].nxt)
        {
            int to = E[j].to;
            if(to!=S&&!use[to-m])
            {
                g=0;
                break;
            }
        }
        if(g)printf("%d ",i);
    }
    printf("\n");
    for(int i=1;i<=n;i++)
        if(use[i])
            printf("%d ",i);
    printf("\n%d\n",sum-ans);
    return 0;
}

 

posted @ 2018-11-21 15:07  LiGuanlin  阅读(199)  评论(0编辑  收藏  举报