FZU - 2295 Human life (最大權閉合子圖)

来源:https://www.cnblogs.com/winter-bamboo/archive/2019/08/10/11332667.html
-Advertisement-
Play Games

題目鏈接 FZU - 2295 Human life 題目分析 題意:你在玩一個游戲,在其中你可以通過學習一些技能,但是學習某些技能之前,可能還要學習一些其他的技能,並且學習任何技能都有一定的花費; 而我們可以通過掌握某些工作以獲取報酬,為了掌握這一工作,我們必須學會特定的技能。 不過有些工作彼此之 ...


題目鏈接

FZU - 2295 Human life

題目分析

題意:你在玩一個游戲,在其中你可以通過學習一些技能,但是學習某些技能之前,可能還要學習一些其他的技能,並且學習任何技能都有一定的花費;

而我們可以通過掌握某些工作以獲取報酬,為了掌握這一工作,我們必須學會特定的技能。

不過有些工作彼此之間是衝突的,簡單來說:如果你掌握了工作A,那麼將無法掌握工作B

思路:

由於技能之間也存在依賴關係,但實際上如果要獲取某一工作的報酬,那麼必須選擇這個工作的前置技能以及前置技能的前置技能,

那麼顯然,這些形成依賴關係的技能都是這一工作的前置技能,所以我們的問題就是求最大權閉合子圖了。

我們在工作和其所有的前置技能之間建一條容量為inf的邊,然後由所有的技能向匯點建一條容量為這一技能學習消耗的邊,

再由源點向每個工作建一條容量為這一工作報酬的邊。

這個地方還加上了一個條件,有些工作無法同時獲取,不過這個地方產生衝突最大對數為5個,那麼我們枚舉所有的情況就好了,

一共2^5 = 32種,根據每種狀態來決定可選取的工作,並構建這一頂點及其相關的邊

然後根據:最大閉合子圖的權值 = 所有權值為正的結點的權值之和 - 最小割(最大流)求出答案

順便吐糟一下這個題,首先這個OJ的C++環境不是C11標準,也就是說不支持大括弧給結構體變數賦值,比如這樣:

我這個語法寫了近一年了,從未出錯,這個評測機第一次把我這裡卡了,一直CE,還不給出錯誤信息QAQ....

代碼區

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) cout<<"["<<x<<","<<y<<"] "
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int Max = 1e3+ 10;

struct Edge
{
    int to, next, flow;
}edge[Max << 2];

int T, n, m, k;
int s, t;
vector<int>edge_raw[Max];            //記錄原圖邊的關係
int vis[2010][1010];                //vis[i][j]記錄j是否是i的前置結點
int select[2010];                    //記錄某點是否被刪除(處理工作衝突)
int use[1010];                        //記錄每個點的是否使用(處理技能之間的前置關係)
int head[2010], tot;
int cost[1010];                        //記錄了學習每個技能的花費
int val[2010];                        //記錄掌握每個工作的報酬
int dis[2010];                        //記錄i的層次編號(Dinic中使用)
pair<int, int>fight[10];            //記錄衝突

void init()
{
    s = 0, t = n + m + 1;            //1-m為技能,m+1~n+m為工作
    memset(vis, 0, sizeof(vis));
    memset(use, 0, sizeof(use));
    for (int i = 0;i <= n; i++)
        edge_raw[i].clear();
}

void add(int u, int v, int flow)
{
    edge[tot].to = v;
    edge[tot].flow = flow;
    edge[tot].next = head[u];
    head[u] = tot++;

    edge[tot].to = u;
    edge[tot].flow = 0;
    edge[tot].next = head[v];
    head[v] = tot++;
}

void dfs(int u)                            //找到每個技能所有的前置技能
{
    use[u] = true;                        //代表u已經訪問
    for (int i = 0; i < edge_raw[u].size(); i++)
    {
        int v = edge_raw[u][i];
        vis[u][v] = true;
        if (!use[v])                    //自己是自己的前置結點
        {
            dfs(v);
        }
        for (int j = 1;j <= n;j++)        //枚舉這個點的前置結點
        {
            if (vis[v][j])                //v的前置結點是j,那麼u的前置結點也是j
            {
                vis[u][j] = true;
            }
        }
    }
}


bool bfs()                                //判斷連通性,將圖分層次
{
    queue<int>q;
    memset(dis, -1, sizeof(dis));
    dis[s] = 0;
    q.push(s);
    while (!q.empty())
    {
        int u = q.front();q.pop();

        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if (dis[v] == -1 && edge[i].flow > 0)
            {
                dis[v] = dis[u] + 1;
                q.push(v);
                if (v == t) return true;
            }
        }
    }
    return false;
}

int dfs(int u, int flow_in)
{
    if (u == t) return flow_in;
    int flow_out = 0;                        //實際流出流量
    for (int i = head[u];i != -1;i = edge[i].next)
    {
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].flow > 0)
        {
            int flow_part = dfs(v, min(flow_in, edge[i].flow));
            if (flow_part == 0)continue;    //無法形成增廣路
            flow_in -= flow_part;
            flow_out += flow_part;
            edge[i].flow -= flow_part;
            edge[i ^ 1].flow += flow_part;
            if (flow_in == 0)break;
        }
    }
    return flow_out;
}

int max_flow()
{
    int sum = 0;
    while (bfs())
    {
        sum += dfs(s, inf);
    }
    return sum;
}


int main()
{
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        init();
        for (int i = 1, num;i <= n;i++)
        {
            scanf("%d%d", cost + i, &num);
            for (int j = 1;j <= num;j++)
            {
                int pre;                                //技能i的前置技能點
                scanf("%d", &pre);
                edge_raw[i].push_back(pre);                //構建直系前置技能關係
            }
        }
        for (int i = 1;i <= n;i++)
        {
            if (!use[i])dfs(i);
        }


        for (int i = n + 1, cnt; i <= n + m;i++)        //工作編號n +1 ~n+m
        {
            scanf("%d%d", val + i, &cnt);
            while (cnt--)
            {
                int x;
                scanf("%d", &x);
                vis[i][x] = true;
                for (int j = 1;j <= n;j++)
                {
                    if (vis[x][j])                        //求出工作所有的前置技能
                        vis[i][j] = true;
                }
            }
        }

        for (int i = 0;i < k;i++)
        {
            scanf("%d %d", &fight[i].first, &fight[i].second);
        }
        int max_val = 0;

        for (int state = 0;state < (1 << k);state++)    //枚舉狀態,對應位置為1表示選first,0表示選second
        {
            memset(select, 0, sizeof(select));            //0表示不刪除
            memset(head, -1, sizeof(head));tot = 0;
            for (int i = 0;i < k;i++)
            {
                if ((state >> i) & 1)
                {
                    select[fight[i].second] = true;        //刪除second
                }
                else
                {
                    select[fight[i].first] = true;        //刪除first
                }
            }
            int sum = 0;                                //記錄總價值
            for (int i = 1 + n;i <= n + m;i++)
            {
                if (select[i - n])continue;                //當前狀態下不不選取的工作就不用構建與之有關的邊了
                sum += val[i];
                add(s, i, val[i]);                        //由源點向可選工作構建一條容量為當前工作報酬的邊
                for (int j = 1;j <= n;j++)
                {
                    if (vis[i][j])
                    {
                        add(i, j, inf);                    //有工作向其所有前置技能點建一條容量為inf的邊
                    }
                }
            }
            for (int i = 1;i <= n;i++)                    //由所有技能向匯點構建一條容量為其花費的邊
            {
                add(i, t, cost[i]);
            }
            int flow = max_flow();
            max_val = max(max_val, sum - flow);            //最大閉合子圖的權值 = 所有權值為正的結點的權值之和 - 最小割(最大流)
        }
        printf("%d\n", max_val);
    }
    return 0;
}
FZU 2295
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前段時間工作上比較忙,這篇文章一直沒來得及寫,本文是閱讀《Java8實戰》的時候,瞭解到Java 8里已經提供了一個非同步非阻塞的介面(CompletableFuture),可以實現簡單的響應式編程的模式,因此用這篇文章做個梳理。我是帶著下麵這幾個問題去學習CompletableFuture這個介面的 ...
  • 實現增量數據索引 上一節中,我們為實現增量索引的載入做了充足的準備,使用到 開源組件來實現MySQL 的binlog監聽,關於binlog的相關知識,大家可以自行網路查閱。或者可以 本節我們將根據binlog 的數據對象,來實現增量數據的處理,我們構建廣告的增量數據,其實說白了就是為了在後期能把廣告 ...
  • 開發板:正點原子STM32F4探索者 (2019-08-10 22:04:39) 開發環境:MDK5.28.0.0 + STM32CubeMX5.3.0 + STM32CubeF4 V1.24.0 內容:使用STM32Cube配置LED0和UART1,實現LED0閃爍和UART1發送 STM32Cu ...
  • 學習環境:Windows10 + QT5.13 + QT Creater4.9.1(2019-08-10 22:02:30) 1.基本工程創建操作 常規操作創建畫面,可選擇QDialog、MainWindow、QWidget三種類型。可選擇直接創建相應的 ui 文件,控制項的添加可以在編輯模式下使用代 ...
  • 類和對象 java 是面向對象的語言 即 萬物皆對象c語言是面向過程語言 一、怎麼去描述一個對象? (1)..靜態的(短時間內不會改變的東西) 例如:外觀,顏色,品牌 (2).動態的(動作) 可以乾什麼:播放音樂,電影 二、java中,描述一個對象從兩方面出發 (1).靜態(屬性)姓名 年齡 籍貫 ...
  • 首先我們先來看看Map集合獲取元素的三種常見方法(1)entrySet(),(2)keySet(),(3)values() 1. entrySet():(1)先返回map集合的所有"映射"的Set集合,這裡規範每個"映射"的類型為Map.Entry<K, V> (2)再通過迭代取出所有的“映射”,再 ...
  • 答案:階梯數為119。 note:該題的答案,只有119,即程式中的 i 的限定值放大至無限大,最終只有當 i = 16,即 x = 7*(16+1) = 119時,才是正確答案。有興趣的同學可以自己親測一下。 ...
  • 點乘和矩陣乘的區別: 1)點乘(即“ \ ”) 各個矩陣對應元素做乘法 若 w 為 m\ 1 的矩陣,x 為 m\ n 的矩陣,那麼通過點乘結果就會得到一個 m\ n 的矩陣。 若 w 為 m\ n 的矩陣,x 為 m\ n 的矩陣,那麼通過點乘結果就會得到一個 m\ n 的矩陣。 w的列數 只能為 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...