【P2762】 太空飛行計劃問題 最大權閉合子圖

題目描述

W 教授正在爲國家航天中心計劃一系列的太空飛行。每次太空飛行可進行一系列商業性實驗而獲取利潤。現已確定了一個可供選擇的實驗集合E={E1,E2,…,Em},和進行這些實驗需要使用的全部儀器的集合I={I1,I2,…In}。實驗Ej需要用到的儀器是I的子集RjÍI。配置儀器Ik的費用爲ck美元。實驗Ej的贊助商已同意爲該實驗結果支付pj美元。W教授的任務是找出一個有效算法,確定在一次太空飛行中要進行哪些實驗並因此而配置哪些儀器才能使太空飛行的淨收益最大。這裏淨收益是指進行實驗所獲得的全部收入與配置儀器的全部費用的差額。

對於給定的實驗和儀器配置情況,編程找出淨收益最大的試驗計劃。

輸入輸出格式

輸入格式:

第1行有2 個正整數m和n。m是實驗數,n是儀器數。接下來的m 行,每行是一個實驗的有關數據。第一個數贊助商同意支付該實驗的費用;接着是該實驗需要用到的若干儀器的編號。最後一行的n個數是配置每個儀器的費用。

輸出格式:

第1 行是實驗編號;第2行是儀器編號;最後一行是淨收益。

輸入輸出樣例

輸入樣例#1:

2 3
10 1 2
25 2 3
5 6 7

輸出樣例#1:

1 2
1 2 3
17

題解

先吐槽一下這一個輸入,調了好長時間還是輸不進來.最後翻的題解才碼出來,還是我太菜了.。
說一下題解:
這是一個最大權閉合圖(好繞口)…
什麼是最大權閉合圖呢?
就是對於一個圖的一個子圖,對於節點u ,那麼它的後繼節點v 也屬於這個子圖,那麼這個子圖就是閉合圖。當這個子圖的權最大時就叫最大權閉合圖.
對於最大權閉合圖,我們有一下的性質:
最小割所產生的兩個集合中,其源點S所在集合(除去S)爲最大權閉合圖。
答案數等於靠近源點最小割一邊的點數,最大利益=所有點正權值之和-最小割.
所以這個題就要用最小割來寫了。
建圖:
S->實驗:邊權爲實驗費用
實驗->所用到的器材:邊權爲INF
器材->T:邊權爲器材費用
跑一邊最大流就可以了(最大流等於最小割)

代碼

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define INF 0x7f7f7f7f
const int MAXN=1e6;
using namespace std;
struct Edge
{
    int next,to,c,f;
}edge[MAXN];
int Num1,Num2,s,t,n,m;
int head[MAXN],level[MAXN],cnt;
int val[MAXN],cost[MAXN];
inline int read()
{
    int 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();
    }
    return x*f;
}
inline bool EXrnum(int &res) //遇到'\n'跳出的讀入優化
{
    res = 0; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '\n') return false; ch = getchar(); }
    while (isdigit(ch)) { res = res * 10 + ch - '0'; ch = getchar(); }
    if (ch == '\n') return false; else return true;
}
inline void Add_Edge(int u,int v,int c,int f)
{
    edge[cnt]=(Edge){head[u],v,c,f};
    head[u]=cnt++;
} 
bool bfs()
{
    memset(level,0,sizeof(level));
    level[s]=1;
    queue<int> que;
    que.push(s);
    while(!que.empty())
    {
        int c=que.front();
        que.pop();
        for(int i=head[c];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(level[v]==0 && edge[i].c>edge[i].f)
            {
                level[v]=level[c]+1;
                que.push(v);    
            }   
        } 
    }
    if(level[t]==0)
        return 0;
    else
        return 1;
}
int dfs(int u,int f)
{
    if(u==t) return f;
    int adf=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(level[v]==level[u]+1 && edge[i].c>edge[i].f)
        {
            int temp=dfs(v,min(f-adf,edge[i].c-edge[i].f));
            edge[i].f+=temp;
            edge[i^1].f-=temp;
            adf+=temp;
        }
    }
    return adf;
}
int Dinic()
{
    int maxf=0;
    while(bfs())
    {
        maxf+=dfs(s,1e9);
    }   
    return maxf;
}
int sum; 
int main()
{
    memset(head,-1,sizeof(head));
    m=read(),n=read();
    s=0,t=n+m+1;    
    for(int i=1;i<=m;i++)
    {
        char ch;
        scanf("%d",&val[i]);
        sum+=val[i];
        Add_Edge(s,i,val[i],0);
        Add_Edge(i,s,0,0); 
        int now=0;
        while(EXrnum(now))
        {
            Add_Edge(i,now+m,INF,0);
            Add_Edge(now+m,i,0,0); 
        } 
        Add_Edge(i,now+m,INF,0);
        Add_Edge(now+m,i,0,0);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        Add_Edge(i+m,t,cost[i],0);
        Add_Edge(t,i+m,0,0); 
    }
    int ans=Dinic();
    for(int i=1;i<=m;i++) 
    {
        if(level[i]!=0)
            printf("%d ",i);
    }
    printf("\n");
    for(int i=1;i<=n;i++)
    {
        if(level[i+m]!=0)
            printf("%d ",i);
    }
    printf("\n");
    printf("%d",sum-ans);
    return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章