1. 程式人生 > >【COGS743】最長k可重區間集問題 最大權不相交路徑

【COGS743】最長k可重區間集問題 最大權不相交路徑

題目描述 Description

給定實直線L 上n 個開區間組成的集合I,和一個正整數k,試設計一個演算法,從開區
間集合I 中選取出開區間集合S屬於I,使得在實直線L 的任何一點x,S 中包含點x 的開區間
個數不超過k,且sum(| z |) z屬於S,達到最大。這樣的集合S稱為開區間集合I的最長k可重區間集。
sum(| z |) z屬於S稱為最長k可重區間集的長度。

對於給定的開區間集合I和正整數k,計算開區間集合I的最長k可重區間集的長度。

輸入描述 Input Description

第1 行有2 個正整數n和k,分別表示開區間的
個數和開區間的可重迭數。接下來的n行,每行有2個整數,表示開區間的左右端點座標。

輸出描述 Output Description

將計算出的最長k可重區間集的長度輸出

樣例輸入 Sample Input

4 2
1 7
6 8
7 10
9 13

樣例輸出 Sample Output

15

資料範圍及提示 Data Size & Hint

若把每個線段看做點,一組不相交的線段集合就可以看做路徑。這樣問題就轉化為選k條路徑使得權值和最大,並且路徑不能重疊(一個線段只能選一次)。

方法1

按左端點排序所有區間,把每個區間拆分看做兩個頂點<i.a><i.b>,建立附加源S匯T,以及附加頂點S’。

1、連線S到S’一條容量為K,費用為0的有向邊。
2、從S’到每個<

i.a>連線一條容量為1,費用為0的有向邊。
3、從每個<i.b>到T連線一條容量為1,費用為0的有向邊。
4、從每個頂點<i.a><i.b>連線一條容量為1,費用為區間長度的有向邊。
5、對於每個區間i,與它右邊的不相交的所有區間j各連一條容量為1,費用為0的有向邊。

求最大費用最大流,最大費用流值就是最長k可重區間集的長度。

這種方法相對與方法2來說比較好想。拆點(計算選邊的價值),容量限制(k個,以及不相交)。

方法2

離散化所有區間的端點,把每個端點看做一個頂點,建立附加源S匯T。

1、從S到頂點1(最左邊頂點)連線一條容量為K,費用為0的有向邊。
2、從頂點2N(最右邊頂點)到T連線一條容量為K,費用為0的有向邊。
3、從頂點i到頂點i+1(i+1<=2N),連線一條容量為無窮大,費用為0的有向邊。
4、對於每個區間[a,b],從a對應的頂點i到b對應的頂點j連線一條容量為1,費用為區間長度的有向邊。

求最大費用最大流,最大費用流值就是最長k可重區間集的長度。

轉換思路:端點為點。同樣是限制流量。而這次目標針對了【每個點至多出現k次】,而區間則成了一段有價值的點,所以可以端點之間連邊來表示。並且這個做法邊數更少,更優。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;

const int INF = 1000000010;
const int SZ = 1000010;

int head[SZ],nxt[SZ],tot = 1;

struct edge{
    int f,t,d,c;
}l[SZ];

void build(int f,int t,int d,int c)
{
    l[++ tot].t = t;
    l[tot].d = d;
    l[tot].f = f;
    l[tot].c = c;
    nxt[tot] = head[f];
    head[f] = tot;
}

void insert(int f,int t,int d,int c)
{
    build(f,t,d,c); build(t,f,0,-c);
}

int dist[SZ];
deque<int> q;
bool use[SZ];
int pre[SZ];

bool spfa(int s,int e)
{
    use[s] = 1;
    q.push_front(s);
    for(int i = 0;i <= 100000;i ++)
        dist[i] = -INF;
    dist[s] = 0;
    while(q.size())
    {
        int u = q.front(); q.pop_front();
        use[u] = 0;
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(l[i].d && dist[v] < dist[u] + l[i].c)
            {
                dist[v] = dist[u] + l[i].c;
                pre[v] = i;
                if(!use[v])
                {
                    use[v] = 1;
                    if(q.empty()) q.push_front(v);
                    else if(dist[q.front()] > dist[v])
                        q.push_front(v);
                    else
                        q.push_back(v);
                }
            }
        }
    }
    if(dist[e] == -INF) return false;
    return true;
}

int dfs(int s,int e)
{
    int x = INF,ans = 0;
    for(int i = pre[e];i;i = pre[l[i].f])
        x = min(x,l[i].d);
    for(int i = pre[e];i;i = pre[l[i].f])
        ans += x * l[i].c,l[i].d -= x,l[i ^ 1].d += x;
    return ans;
}

int ek(int s,int e)
{
    int ans = 0;
    while(spfa(s,e)) ans += dfs(s,e);
    return ans;
}

struct haha{
    int l,r;
}seg[SZ];

bool cmp(haha a,haha b)
{
    return a.l < b.l;
}


int main()
{
    freopen("interv.in","r",stdin);
    freopen("interv.out","w",stdout);   
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i ++)
        scanf("%d%d",&seg[i].l,&seg[i].r);
    sort(seg + 1,seg + 1 + n,cmp);

    int S = n * 2 + 1,S1 = n * 2 + 2,T = n * 2 + 3;

    insert(S,S1,k,0);

    for(int i = 1;i <= n;i ++)
        insert(S1,i,1,0),insert(i + n,T,1,0),insert(i,i + n,1,seg[i].r - seg[i].l);

    for(int i = 1;i <= n;i ++)
    {
        for(int j = i + 1;j <= n;j ++)
        {
            if(seg[i].r <= seg[j].l)
            {
                insert(i + n,j,1,0);
            }
        }
    }


    printf("%d\n",ek(S,T));
    return 0;
}

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;

const int INF = 1000000010;
const int SZ = 1000010;

int head[SZ],nxt[SZ],tot = 1;

struct edge{
    int f,t,d,c;
}l[SZ];

void build(int f,int t,int d,int c)
{
    l[++ tot].t = t;
    l[tot].d = d;
    l[tot].f = f;
    l[tot].c = c;
    nxt[tot] = head[f];
    head[f] = tot;
}

void insert(int f,int t,int d,int c)
{
    build(f,t,d,c); build(t,f,0,-c);
}

int dist[SZ];
deque<int> q;
bool use[SZ];
int pre[SZ];

bool spfa(int s,int e)
{
    use[s] = 1;
    q.push_front(s);
    for(int i = 0;i <= 100000;i ++)
        dist[i] = -INF;
    dist[s] = 0;
    while(q.size())
    {
        int u = q.front(); q.pop_front();
        use[u] = 0;
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(l[i].d && dist[v] < dist[u] + l[i].c)
            {
                dist[v] = dist[u] + l[i].c;
                pre[v] = i;
                if(!use[v])
                {
                    use[v] = 1;
                    if(q.empty()) q.push_front(v);
                    else if(dist[q.front()] > dist[v])
                        q.push_front(v);
                    else
                        q.push_back(v);
                }
            }
        }
    }
    if(dist[e] == -INF) return false;
    return true;
}

int dfs(int s,int e)
{
    int x = INF,ans = 0;
    for(int i = pre[e];i;i = pre[l[i].f])
        x = min(x,l[i].d);
    for(int i = pre[e];i;i = pre[l[i].f])
        ans += x * l[i].c,l[i].d -= x,l[i ^ 1].d += x;
    return ans;
}

int ek(int s,int e)
{
    int ans = 0;
    while(spfa(s,e)) ans += dfs(s,e);
    return ans;
}

struct haha{
    int l,r;
}seg[SZ];

int lsh[SZ];

int main()
{
    freopen("interv.in","r",stdin);
    freopen("interv.out","w",stdout);       
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i ++)
    {
        scanf("%d%d",&seg[i].l,&seg[i].r);
        lsh[++ lsh[0]] = seg[i].l;
        lsh[++ lsh[0]] = seg[i].r;
    }
    sort(lsh + 1,lsh + 1 + lsh[0]);
    int m = unique(lsh + 1,lsh + 1 + lsh[0]) - lsh - 1;


    int S = m + 1,T = m + 2;

    insert(S,1,k,0); insert(m,T,k,0);

    for(int i = 1;i < m;i ++)
        insert(i,i + 1,INF,0);

    for(int i = 1;i <= n;i ++)
    {
        int x = lower_bound(lsh + 1,lsh + 1 + m,seg[i].l) - lsh;
        int y = lower_bound(lsh + 1,lsh + 1 + m,seg[i].r) - lsh;
        insert(x,y,1,seg[i].r - seg[i].l);
    }

    printf("%d\n",ek(S,T));
    return 0;
}

相關推薦

COGS743k區間問題 大權相交路徑

題目描述 Description 給定實直線L 上n 個開區間組成的集合I,和一個正整數k,試設計一個演算法,從開區 間集合I 中選取出開區間集合S屬於I,使得在實直線L 的任何一點x,S 中包含點x 的開區間 個數不超過k,且sum(| z |) z屬於

nefu495k區間問題大權相交路徑網路流24題

本來是應該昨天晚上就寫完的,果然在家的狀態不好==而且以後就應該11點半之前就睡,腦子不靈光寫字都不過腦子還不如睡覺~。~據說今年國賽有5站,留下來問題應該不大,但是能拿什麼獎就不好說了,總之要加油。。方法二沒看懂,最大權不相交路徑太難了,24題裡面只有兩個,還都是4星的=

k區間問題

ace blog register 一個 inf for wap ans string 最長k可重區間集問題 題目鏈接 https://www.luogu.org/problemnew/show/3358 做法 所有點向下一個點連容量為k費用為0的邊 l和r連容量為1費用為

「網絡流24題」「LuoguP3358」 k區間問題

取反 spa 區間 out freopen clu 內存 ted sizeof 題目描述 對於給定的開區間集合 I 和正整數 k,計算開區間集合 I 的最長 k可重區間集的長度。 輸入輸出格式 輸入格式: 的第 1 行有 2 個正整數 n和 k,分別表示開區

網路流24題 21k區間問題

最長k可重區間集問題 Time Limit 1000ms Memory Limit 65536K description 給定實直線L 上n 個開區間組成的集合I,和一個正整數k,試設計一個演算法,從開區間集合I 中選取出開區間集合S屬

「Luogu3358」 k區間問題

n+1 fin getc std 線段 front 起點 typename cst 「Luogu3358」 最長k可重區間集問題 problem Solution 最大費用最大流模型。 約定:下文采用格式\((u,v,f,c)\)表示以\(u\)為起點,\(v\)為終點,\

網絡流 P3358 k區間問題

size 分享圖片 cos 離散化 復制 正向 ron \n 離散 P3358 最長k可重區間集問題 題目描述 對於給定的開區間集合 I 和正整數 k,計算開區間集合 I 的最長 k可重區間集的長度。 輸入輸出格式 輸入格式: 的第 1 行有 2 個正整

網絡流24題22k線段問題

ans http ret 網絡流24題 math 離散化 cpp main 擴大 題面戳我 題面自己去看(我懶得搞這些markdown) 成功成為繼ppl之後第二個在洛谷上AC這道題的ID。ppl把這題的AC率從0%提升到了2.5%,提升了無數倍,ppl果然是墜強的!這裏先

洛谷 P3357 k線段問題大流

img 分享圖片 math sqrt 最長 .html getchar -m wap pre:http://www.cnblogs.com/lokiii/p/8435499.html 和最長k可重區間集問題差不多,也就是價值的計算方法不一樣,但是註意這裏可能會有x0==x1

「Luogu3357」 k線段問題

lower span str getc ios iostream .html problem esp 「Luogu3357」 最長k可重線段集問題 problem Solution 與「Luogu3357」 最長k可重區間集問題類似,但此題需要考慮斜率不存在的線段 我們將每

bzoj1061(k區間)

然而還是不能夠很好地理解線性規劃,因此再看了一下發現其實是k可重區間集問題。。 那麼建圖方向就有了。。然而這個題比較特殊,只有下界沒有上界,想跑上下界也不行了。。 因此可以把容量取反,下界就變成上界了。。然後由於網路流只接受正數流,因此要把取反之後的容量加上inf,然

網絡流24題數字梯形問題(費用流)(大權相交路徑

output 提示 正整數 cti 移動 block 完全 amp 方向 1913 數字梯形問題 時間限制: 2 s 空間限制: 256000 KB 題目等級 : 大師 Master

軟體開發底層知識修煉九 連結器-定位檔案與執行檔案

上幾篇文章學習了Binutils輔助工具裡面的幾個實用的工具,那些工具對於以後的學習都是非常有幫助的,尤其是C語、C++語言的學習以及除錯是非常有幫助的。點選連結檢視上一篇文章:點選檢視 本篇文章開始一個新的知識的學習,連結器的學習。學習完連結器的系列文章,我們將全面瞭解連結器的工作

ShawnZhang帶你看藍橋杯——演算法提高 單詞

該題通過本提示通過列舉演算法解決,恕本人愚鈍,不太清楚,就用了自己的方法,程式碼附後 遇到的幾個問題,如果使用String s=in.next()將無法儲存空格後的內容,如I am a student,通過這個函式只能儲存第一個單詞  I(單詞意思:我,大寫的i),所以我

部分轉載lower_bound、upperbound講解、二分查詢、上升子序列(LIS)、下降子序列模版

二分 lower_bound lower_bound()在一個區間內進行二分查詢,返回第一個大於等於目標值的位置(地址) upper_bound upper_bound()與lower_bound()的主要區別在於前者返回第一個大於目標值的位置 int lowerBound(int x){ i

BZOJ3698XWW的難題 有上下界的大流

head 上界 需要 ace min 1.2 3.6 queue 取整 【BZOJ3698】XWW的難題 Description XWW是個影響力很大的人,他有很多的追隨者。這些追隨者都想要加入XWW教成為XWW的教徒。但是這並不容易,需要通過XWW的考核。XWW給

BZOJ4950lydsy七月月賽 C 二分圖大匹配

for 但是 需要 com 成了 strong div mic printf 【BZOJ4950】lydsy七月月賽 C 題面 題解:比較直接的想法就是:每行,每列的最大值都留下,剩下的格子都變成1。但是如果一個格子既是行的最大值又是列的最大值,那麽我們只需要把它留下即

MT16利用柯西不等式求三角的大值

技術分享 com style img 不等式 bsp nbsp png 均值 評:此題也可以設$1+cos\theta=t$,平方後變成$t$的單變量利用均值去做. 柯西平衡系數法其實就是待定系數法,利用等號取到的條件。MT【16】利用柯西不等式求三角的最大值

MT61含參數二次函數小值

tco pla 最大 back inline 我們 最小 但是 alt 評:此類題目在高考中作為壓軸題也曾考過,一般通性通法都如上面的做法,但是我們如果可以站在包絡的角度,很多問題將變得很清晰:MT【61】含參數二次函數最大最小值

經驗電腦時間關機導致的無法開機問題

啟動菜單 但是 can win7 一段 分鐘 硬盤 size 開機 本來不打算寫電腦掛掉這種問題的記錄,但是我這個問題比較特殊還遇到兩次,直接送電腦店可能會說主板掛了,返廠檢修之類的,對於緊急用電腦的人來說是不可接受的,其實分分鐘就可以解決。 1.環境:用了7年的宏碁475