hdu5352 2015多校第5場 網絡流(或二分圖)

鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5352


題意:有N個城市,一開始都處於被地震摧毀的狀態。之後有M個詢問,1 x表示可以將與x相連(包括x)的城市重建(重建後不再被地震破壞),每次能重建的城市的數量小於等於k;2 x y表示在x和y之間建立一條道路相同;3 p x1 y1...xp yp表示有p組道路被破壞,分別是x1 y1...xp yp;現要求每次詢問1 x時要重建幾座城市,使得所有最後重建的城市的數量最多,且重建城市的數量形成的序列的字典序最小。


想法:

感覺是挺難想到的網絡流問題,主要就是模型的確立(網絡流難的一般都是建立模型)。

大致的建圖方式:將所有城市與源點s相連,邊的容量爲1(爲什麼爲1?因爲城市只能被重建一次,建好後便不會被摧毀),費用爲0。將所有1 x的操作弄成節點與匯點t相連,容量爲k(因爲每次最多隻能重建k個城市),費用爲0。並且每次1 x的操作中,將該節點和x聯通的節點都連一條邊,邊的容量爲無窮大,邊的費用是cost(這個等下稍微要有點技巧),就相當於每次的1 x的操作要重建幾座城市。


如果題目要求是的最多建立幾座城市,那麼最大流就夠了,但是這裏要用到最小費用最大流來實現字典序最小。假設cost一開始是一個較大的值,每次操作1 x之後這個cost就自減一個值,這樣做的目的是讓流量優先通過後面操作的1 x,來實現字典序的最小。要輸出答案的話,只要遍歷1 x的節點,將通向匯點的邊的流量記錄下來輸出就是最小字典序




#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int e_maxn = 150000 * 2;
const int v_maxn = 900;
struct ppp
{
    int v,nex,cap,flow,c;
}e[e_maxn];
int head[v_maxn],pre[v_maxn],inq[v_maxn],a[v_maxn],dis[v_maxn];
int tole,N,M,s,t,K;
void make_edge(int u,int v,int cap,int c)
{
    e[tole].v = v;e[tole].cap = cap;e[tole].c = c;e[tole].flow = 0;e[tole].nex = head[u]; head[u] = tole++;
}
void add_edge(int u,int v,int cap,int c)
{
    make_edge(u,v,cap,c);
    make_edge(v,u,0,-c);
}
queue<int> que;
void maxflow_mincost(int s,int t,int &flow,int &cost)
{
    int temp,v,u;
    while(1)
    {
        mem(inq,0);
        mem(dis,0x3f);
        a[s] = INF;
        dis[s] = 0;
        inq[s] = 1;
        que.push(s);
        mem(pre,-1);
        pre[s] = 0;
        while(!que.empty())
        {
            temp = que.front();
            que.pop();
            inq[temp] = 0;
            for(int i = head[temp];~i;i = e[i].nex)
            {
                v = e[i].v;
                if(e[i].cap > e[i].flow && dis[v] > dis[temp] + e[i].c)
                {
                    dis[v] = dis[temp] + e[i].c;
                    pre[v] = i;
                    a[v] = min(a[temp],e[i].cap - e[i].flow);
                    if(!inq[v]){que.push(v);inq[v] = 1;}
                }
            }
        }
        if(pre[t] == -1)break;
        flow += a[t];
        cost += dis[t] * a[t];
        for(u = t;u != s;u = e[pre[u] ^ 1].v)
        {
            e[pre[u]].flow += a[t];
            e[pre[u] ^ 1].flow -= a[t];
        }
    }
}
int ma[202][202];
int vis_v[202];
void dfss(int u)//找於x相連的點用dfs
{
    for(int i = 1; i <= N;i++)
        if(!vis_v[i] && ma[u][i])
    {
        vis_v[i] = 1;
        dfss(i);
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        mem(head,-1);
        tole = 0;
        scanf("%d%d%d",&N,&M,&K);
        int cntk = 0;
        mem(ma,0);
        int a,b,c;
        int cost = 9999999;//cost在每次1 x的操作後自減
        while(M--)
        {
            scanf("%d",&a);
            if(a == 1){
                scanf("%d",&b);
                mem(vis_v,0);
                vis_v[b] = 1;
                dfss(b);
                cntk++;
                for(int i = 1;i <= N;i++)
                if(vis_v[i])
                    add_edge(i,N + cntk,INF,cost);
                cost -= 100;
            }else if(a == 2){
                scanf("%d%d",&b,&c);
                ma[b][c] = 1;
                ma[c][b] = 1;
            }else if(a == 3){
                scanf("%d",&a);
                for(int i = 1;i <= a;i++)
                {
                    scanf("%d%d",&b,&c);
                    ma[b][c] = ma[c][b] = 0;
                }
            }
        }
        s = 0,t = N + cntk + 1;
        for(int i = 1;i <= N;i++)
            add_edge(s,i,1,0);
        for(int i = N + 1;i <= N + cntk;i++)
            add_edge(i,t,K,0);
        int haha = 0,xixi = 0;
        maxflow_mincost(s,t,haha,xixi);
        int ans[505];
        int len = 0;
        int summ = 0;
        for(int i = N + 1;i <= N + cntk;i++)
        {
            for(int j = head[i];~j;j = e[j].nex)
            {
                int v = e[j].v;
                if(v == t)
                {
                    summ += e[j].flow;
                    ans[len++] = e[j].flow;
                    break;
                }
            }
        }
        printf("%d\n",summ);
        for(int i = 0;i < len;i++)
        {
            if(i > 0){
                printf(" ");
            }
            printf("%d",ans[i]);
        }
        printf("\n");
    }
}


發佈了50 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章