1. 程式人生 > >hdu1565 方格取數(1)&&hdu1569 方格取數(2)(最小割)

hdu1565 方格取數(1)&&hdu1569 方格取數(2)(最小割)

題意:中文題不解釋。

思路:最大點權獨立集。注意二分匹配能處理的是最大點獨立集,帶權的就只能用最大流了。剛開始看是求取出的數和最大,那直接用最大流唄,不知道怎麼建邊。看別人的才知道這些帶權的都是獨立點,我們是無法對獨立點求最大流的。最大點權獨立集=點權總和-最小點權覆蓋集。二分匹配中最小頂點覆蓋集是最大匹配數,這裡又是什麼鬼?其實是最小割值。

最小割的意義就是求取數方案的最小花費(前提是能取的都取,一個也不取不能算最小花費),同樣滿足相鄰各自不能取(由於相鄰各自不能取,只有兩種取數方案)。這題由於上下左右相鄰的數不能取,所以按照國際棋盤染色規律分為兩個集合,那麼這兩個集合一個和源點s相連,一個和匯點t相連,邊的權值就是數的值。一條路上兩種元素滿足互斥關係(有你沒我),這就是判斷是否是最小割問題的關鍵點!但是這個不同於上一個任務被執行,割一條邊就行,這個是兩個元素,雖然兩個邊同時不被割不可能,但是同時被割完全有可能啊!所以中間需要連一條無窮大的邊,代表要想同時被割需要付出無窮大的代價。這是這道題比較特殊的一點。其他的思路就和上一題一樣了。

有了思路知道怎麼建圖,這題就該被秒殺了。

#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>

using namespace std;

typedef long long LL;

const int N = 505;
const int INF = 0x3f3f3f3f;

int head[N], dis[N], cur[N], G[N][N];
bool vis[N];
int n, m, cnt, sum;

struct Edge
{
    int to, cap, flow, next;
}edge[N*N];

void add(int u, int v, int w)
{
    edge[cnt] = (struct Edge){v, w, 0, head[u]};
    head[u] = cnt++;
    edge[cnt] = (struct Edge){u, 0, 0, head[v]};
    head[v] = cnt++;
}

bool bfs(int start, int endd)
{
    memset(dis, -1, sizeof(dis));
    memset(vis, false, sizeof(vis));
    queue<int>que;
    dis[start] = 0;
    vis[start] = true;
    que.push(start);
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.flow<E.cap)
            {
                dis[E.to] = dis[u]+1;
                vis[E.to] = true;
                if(E.to == endd) return true;
                que.push(E.to);
            }
        }
    }
    return false;
}


int dfs(int x, int res, int endd)
{
	if(x == endd || res == 0) return res;
	int flow = 0, f;
	for(int& i = cur[x]; i != -1; i = edge[i].next)
	{
		Edge E = edge[i];
		if(dis[E.to] == dis[x]+1)
		{
		    f = dfs(E.to, min(res, E.cap-E.flow), endd);
            if(f>0)
            {
                edge[i].flow += f;
                edge[i^1].flow -= f;
                flow += f;
                res -= f;
                if(res == 0) break;
            }
		}
	}
	return flow;
}


int max_flow(int start, int endd)
{
    int flow = 0;
    while(bfs(start, endd))
    {
        memcpy(cur, head, sizeof(head));
        flow += dfs(start, INF, endd);
    }
    return flow;
}

void init()
{
    cnt = 0;
    memset(head, -1, sizeof(head));
}

void getmap()
{
    int s = 0, t = n*n+1;
    sum = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            scanf("%d", &G[i][j]);
            sum+=G[i][j];
            if((i+j)%2 == 1)
            {
                add(s, (i-1)*n+j, G[i][j]);
                if(j>1) add((i-1)*n+j, (i-1)*n+j-1, INF);
                if(j<n) add((i-1)*n+j, (i-1)*n+j+1, INF);
                if(i>1) add((i-1)*n+j, (i-1-1)*n+j, INF);
                if(i<n) add((i-1)*n+j, (i-1+1)*n+j, INF);
            }
            else
            {
                add((i-1)*n+j, t, G[i][j]);
            }
        }
}

int main()
{
  //  freopen("in.txt", "r", stdin);
    while(~scanf("%d", &n))
    {
        init();
        getmap();
        int ans = max_flow(0, n*n+1);
        printf("%d\n", sum-ans);
    }
    return 0;
}

題意思路和上面一樣,改個輸入輸出就行。

#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>

using namespace std;

typedef long long LL;

const int N = 2505;
const int INF = 0x3f3f3f3f;

int head[N], dis[N], cur[N], G[N][N];
bool vis[N];
int n, m, cnt, sum;

struct Edge
{
    int to, cap, flow, next;
}edge[N*N];

void add(int u, int v, int w)
{
    edge[cnt] = (struct Edge){v, w, 0, head[u]};
    head[u] = cnt++;
    edge[cnt] = (struct Edge){u, 0, 0, head[v]};
    head[v] = cnt++;
}

bool bfs(int start, int endd)
{
    memset(dis, -1, sizeof(dis));
    memset(vis, false, sizeof(vis));
    queue<int>que;
    dis[start] = 0;
    vis[start] = true;
    que.push(start);
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.flow<E.cap)
            {
                dis[E.to] = dis[u]+1;
                vis[E.to] = true;
                if(E.to == endd) return true;
                que.push(E.to);
            }
        }
    }
    return false;
}


int dfs(int x, int res, int endd)
{
	if(x == endd || res == 0) return res;
	int flow = 0, f;
	for(int& i = cur[x]; i != -1; i = edge[i].next)
	{
		Edge E = edge[i];
		if(dis[E.to] == dis[x]+1)
		{
		    f = dfs(E.to, min(res, E.cap-E.flow), endd);
            if(f>0)
            {
                edge[i].flow += f;
                edge[i^1].flow -= f;
                flow += f;
                res -= f;
                if(res == 0) break;
            }
		}
	}
	return flow;
}


int max_flow(int start, int endd)
{
    int flow = 0;
    while(bfs(start, endd))
    {
        memcpy(cur, head, sizeof(head));
        flow += dfs(start, INF, endd);
    }
    return flow;
}

void init()
{
    cnt = 0;
    memset(head, -1, sizeof(head));
}

void getmap()
{
    int s = 0, t = n*m+1;
    sum = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            scanf("%d", &G[i][j]);
            sum+=G[i][j];
            if((i+j)%2 == 1)
            {
                add(s, (i-1)*m+j, G[i][j]);
                if(j>1) add((i-1)*m+j, (i-1)*m+j-1, INF);
                if(j<m) add((i-1)*m+j, (i-1)*m+j+1, INF);
                if(i>1) add((i-1)*m+j, (i-1-1)*m+j, INF);
                if(i<n) add((i-1)*m+j, (i-1+1)*m+j, INF);
            }
            else
            {
                add((i-1)*m+j, t, G[i][j]);
            }
        }
}

int main()
{
  //  freopen("in.txt", "r", stdin);
    while(~scanf("%d%d", &n, &m))
    {
        init();
        getmap();
        int ans = max_flow(0, n*m+1);
        printf("%d\n", sum-ans);
    }
    return 0;
}



相關推薦

hdu1565 方格(1)&&hdu1569 方格(2)

題意:中文題不解釋。 思路:最大點權獨立集。注意二分匹配能處理的是最大點獨立集,帶權的就只能用最大流了。剛開始看是求取出的數和最大,那直接用最大流唄,不知道怎麼建邊。看別人的才知道這些帶權的都是獨立點,我們是無法對獨立點求最大流的。最大點權獨立集=點權總和-最小點權覆

hdu 1565 方格(1)

方格取數(1) Problem Description 給你一個n*n的格子的棋盤,每個格子裡面有一個非負數。 從中取出若干個數,使得任意的兩個數所在的格子沒有公共邊,就是說所取的數所在的2個格子不能相鄰,並且取出的數的和最大。 Input 包括多個測試例項,每個測試例項

洛谷P2774 方格問題

stream [] inf lan true printf () amp cstring 傳送門 考慮一下,答案就是全局和減去舍棄和 不難發現,如果我們按行數+列數的奇偶性分為兩類,那麽每一類中的數必然互不相鄰 那麽我們把原圖的點分為黑點和白點兩類,原地向白點連

網路流24題 P2774 方格問題

題目描述 在一個有 m*n 個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意 2 個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數演算法。對於給定的方格棋盤,按照取數要求程式設計找出總和最大的數。 輸入輸出格式 輸入格式:

HDU 1565 1569 方格

連結:http://acm.hdu.edu.cn/showproblem.php?pid=1565 這兩題其實是同一題,一個數據小,一個數據大,據說資料小的時候能用狀壓DP。哇!好神奇呀,可是我不會。就用dinic搞了一下。 思路:假設選了(x,y)點,那麼

【洛谷2774】 方格問題網絡流24題,

define line sin 方格取數 inline getch string.h 一個 最小割 前言 為什麽他們能夠切的那麽快啊。 Solution 雖然我不會怎麽區分最大流和最小費用最大流,但是最大流可以看成最小割,這樣子就好區分一些。 考慮這個東西相當於是二分圖求一

Codeforces Round #248 (Div. 1) D - Nanami's Power Plant

clas define sign fir bsp codeforce using nic second D - Nanami‘s Power Plant 思路:類似與bzoj切糕那道題的模型。。 #include<bits/stdc++.h> #def

HDU 1565 - 方格(1) - [狀壓DP][網絡流 - 大點權獨立集和點權覆蓋集]

printf 一個 cnblogs ret com bool limit .net amp 題目鏈接:https://cn.vjudge.net/problem/HDU-1565 Time Limit: 10000/5000 MS (Java/Others) Memory

[luoguP2045] 方格加強版費用大流

col pid opened empty spl amp turn define aps 傳送門 水題 ——代碼 1 #include <queue> 2 #include <cstdio>

二分圖點權覆蓋 二分圖大權獨立集 方格

補集 限制 成了 最小 選擇 沒有 構造 最大點權獨立集 棋盤   二分圖最小點權覆蓋:     每一條邊 (u, v) 都是一個限制條件, 要求 u 和 v 不能同時取得.     我們考慮先取得所有的, 然後減去最小的點權.     建立原點 S , 連向二分圖左邊的所

[BZOJ1475]方格 網絡流

play closed esc break .com data con 取數 time 1475: 方格取數 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1025 Solved: 512[Submit][Status][D

【網絡流24題】方格問題大流

ace nic 最小 stdin getch esp mar memset pty 【網絡流24題】方格取數問題(最大流) 題面 Cogs 題解 首先,相鄰的只能出現一個,每個點要麽選,要麽不選。 所以不難想到最小割 所以,將棋盤黑白染色後 將某種顏色的格子從源點連過去,容

洛谷 P2774 方格問題【

log pre 分開 d+ memset etc for markdown char 因為都是正整數,所以當然取得越多越好。先把所有點權加起來,黑白染色後,s向所有黑點連流量為點權的邊,所有白點向t連流量為點權的邊,然後黑點向相鄰的四個白點連流量為inf的邊,表示不可割,這

網路流24題之方格問題 二分圖+

原題連結 題目大意 在一個有\(n\times m\)個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意\(2\)個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數演算法。對於給定的方格棋盤,按照取數要求程式設計找出總和最大的數。 來看看怎麼建圖: 首先我們把

洛谷P2774_方格問題_大流_

題目大意 m * n 的矩陣中取數,不能取相鄰的數,求能取得的數的和最大為多少。 思路 (i, j) 為 i 行 j 列的單元格,根據 i + j 的奇偶性將節點分為兩個集合,在矩陣中相鄰得節點分別在圖的兩側,得一個二分圖。 s 點向左側節點建邊,容量為節點在矩陣中的

方格問題

dig 描述 gcd tac font pan 輸入輸出 n) space 題目背景 none! 題目描述 在一個有 m*n 個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意 2 個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數算法。

/二分圖大獨立集】【網絡流24題】【P2774】 方格問題

getc 如果 size etc lse fine std set solution Description 給定一個 \(n~\times~m\) 的矩陣,每個位置有一個正整數,選擇一些互不相鄰的數,最大化權值和 Limitation \(1~\leq~n,~m~\leq

HDU 3657 Game( )經典

next 是否 sco gree mission problem posit -1 appear Game Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Ot

C++:構造函1——普通構造函

創建 c++編譯 clu namespace 我們 這一 () 一次 ret 前言:構造函數是C+中很重要的一個概念,這裏對其知識進行一個簡單的總結 一、構造函數的定義 1.類中的構造函數名與類名必須相同 2.構造函數沒有函數的返回類值型說明符 [特別註意]: a.構造函數

【原創】給定隨機數的值範圍值、大值,且要求多次取得的隨機數最後的結果有一個固定的平均值

給定隨機數的取值範圍(最小值、最大值),且要求多次取得的隨機數最後的結果有一個固定的平均值。 演算法如下: /****** * author ztg 281099678 2018-12-06 * @param $min float 範圍最小值 * @param $max