HDU 5352 MZL's City 最小費用最大流MCMF
題意:MZL是個女孩,他有一個自己的國家。
她的國家有N個城市,標號從1到N,她已經控制這個國家很長時間了並且她記得M年前發生了一次大地震,使得所有城市之間的道路都被破壞了,並且所有的城市也被摧毀。她還記得在之後M年的每一年發生的一件事情。
這些事情可能是以下三種之一(也就是一年只發生其中之一事件):
1.她重建了與X城市直接或者間接相連並且包括X城市的城市群。(注意:如果一個城市被重建那麼這個城市就不會被再次損毀)
2.城市X和城市Y之間連線的是雙向邊
3.在這M年間的某年,發生了一次地震,地震摧毀了一些路
MZL忘記了確切重建了哪些城市。但是MZL知道每年重建的城市個數不能超過K個。現在她想要知道這個M年間最多可以重建多少個城市。與此同時,她還想知道,在保證最多重建城市個數的前提下,每一次重建城市的個數是多少,並且按照字典序最小輸出。比如說:8年間,其中有3年重建了一些城市,加入最多可以建造11個城市,那麼有可能分配方案是:重建的第一個年頭8,第二個年頭2,第三個年頭1,這三個年頭可以不連續。那麼也有可能的分配方案是:第一個年頭10,第二個年頭0, 第三個年頭1,但是前一個方案是滿足提議的因為 字典序(8, 2, 1) < 字典序(10, 0, 1)。
以上是提議部分,輸入是:
先輸入T,表示輸入的案例個數。
然後輸入N,M,K分別表示,城市的數量,年數,每一次重建的城市,最多建造K個。接下來是M行,每行分別是1,2,3操作(上面已經解釋過了)。對於2操作,緊接著u,v表示重建此路,對於3操作緊接著num,然後有num條路被摧毀,然後是num個u,v。
想法:可以想到最大流,因為有K的限制,對於每年需要建造的操作可以進行很多城市的選擇,但是管道的流量是上限K,這是就可以進行一個大略的建圖:
1.設定source和sink,源點和匯點。
2.每一個城市向sink連線一條容量為1的邊。代表是否建造這個城市。
3.sink向每一次“建設年”的操作連線一條容量為K的邊,表示今年可以建造最多K個城市,也就是0~K個城市都行。然後如果從“建設年” 向 建設年建造的城市以及其可以到達的城市構成的集合中的每一個元素城市連線一條容量為1的邊,表示是否建造這個城市,因為容量只能是0/1,這就很顯然形成了對可以建造城市的數量的選擇。
那麼接下來的問題就是考慮最小的字典序問題。顯然就是能前幾個年頭能不建城市就不建城市,能少建就少建城市,留著之後建設。也就是說盡可能的讓最近“建設年”的城市多重建,也就是尋找增廣路的時候希望最近的“建設年”城市先被增廣。那麼此時就非常容易引入最短路,也就是說增廣時候按照最短路去增廣,給每一條邊加入一個費用,越近的“建設年”費用就低,這樣就完美解決了最小子序列問題。也就是對於建圖的第2條,給每條邊一個費用0,或者統一都是一個量就行,然後給第3條,按照“建造年”的時間順序給一個費用,時間越近費用越低,所以最簡單的就是給一個初值fee = 1000(因為1000>M)然後每到一個“建設年”,fee減去1,這樣構成遞減,至於減多少,自己定,只要夠減500次就行,每次減少至少為1。然後擼一遍最小費用最大流完事,輸出的是最大流。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cstdlib>
#define INF 0x7fffffff
using namespace std;
const int max_node_size = 210;
const int max_op_size = 510;
int N, M, K;
int T;
bool mark_graph[max_node_size][max_node_size], visit[max_node_size];
int ans[max_op_size];
class static_AdjList
{
public:
struct Edge
{
int u;
int v;
int flow;
int fee;
int next;
};
struct Edge *edge;
int *head;
int cnt;
~static_AdjList(){}
void _init(int num_node, int num_edge);
void AddEdge(int u, int v, int flow, int fee);
};
void static_AdjList::_init(int num_node, int num_edge)
{
struct Edge *tmp_edge = new Edge[num_edge];
edge = tmp_edge;
tmp_edge = NULL;
delete[] tmp_edge;
int *tmp_head = new int[num_node];
head = tmp_head;
tmp_head = NULL;
delete[] tmp_head;
cnt = 0;
for(int i = 0; i < num_node; i++)
{
head[i] = -1;
}
}
void static_AdjList::AddEdge(int u, int v, int flow, int fee)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].flow = flow;
edge[cnt].fee = fee;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].flow = 0;
edge[cnt].fee = -fee;
edge[cnt].next = head[v];
head[v] = cnt++;
}
class MCMF
{
public:
static_AdjList sadjlist;
~MCMF(){}
void _init(int num_node, int num_edge, int s, int t);
bool SPFA();
int Get_MCMF();
private:
int *depth;
int *search_back;
int *mark_edgeid;
int min_cost;
int source, sink;
int n_node, n_edge;
};
void MCMF::_init(int num_node, int num_edge, int s, int t)
{
int *tmp_depth = new int[num_node];
depth = tmp_depth;
tmp_depth = NULL;
delete[] tmp_depth;
int *tmp_search_back = new int[num_node];
search_back = tmp_search_back;
tmp_search_back = NULL;
delete[] tmp_search_back;
int *tmp_mark_edgeid = new int[num_node];
mark_edgeid = tmp_mark_edgeid;
tmp_mark_edgeid = NULL;
delete[] tmp_mark_edgeid;
sadjlist._init(num_node, num_edge);
min_cost = 0;
source = s;
sink = t;
n_node = num_node;
n_edge = num_edge;
}
bool MCMF::SPFA()
{
bool *visited_node = new bool[n_node];
queue<int>q;
while(!q.empty()) q.pop();
for(int i = 0; i < n_node; i++)
{
depth[i] = INF;
search_back[i] = -1;
visited_node[i] = false;
}
depth[source] = 0;
visited_node[source] = true;
q.push(source);
while(!q.empty())
{
int cur_node = q.front();
q.pop();
visited_node[cur_node] = false;
for(int i = sadjlist.head[cur_node]; i + 1; i = sadjlist.edge[i].next)
{
int next_node = sadjlist.edge[i].v;
if(depth[next_node] > depth[cur_node] + sadjlist.edge[i].fee && sadjlist.edge[i].flow > 0)
{
depth[next_node] = depth[cur_node] + sadjlist.edge[i].fee;
search_back[next_node] = cur_node;
mark_edgeid[next_node] = i;
if(!visited_node[next_node])
{
visited_node[next_node] = true;
q.push(next_node);
}
}
}
}
delete[] visited_node;
if(depth[sink] != INF) return true;
else return false;
}
int MCMF::Get_MCMF()
{
int ans_mcmf = 0;
while(SPFA())
{
int flow = INF;
for(int cur_node = sink; cur_node != source; cur_node = search_back[cur_node])
{
flow = min(flow, sadjlist.edge[mark_edgeid[cur_node]].flow);
}
for(int cur_node = sink; cur_node != source; cur_node = search_back[cur_node])
{
int edgeid = mark_edgeid[cur_node];
sadjlist.edge[edgeid].flow -= flow;
sadjlist.edge[edgeid ^ 1].flow += flow;
}
ans_mcmf += flow;
}
return ans_mcmf;
}
void dfs(int city, int op_index, MCMF &mcmf)
{
mcmf.sadjlist.AddEdge(op_index, city, 1, 0);
visit[city] = true;
for(int i = 1; i <= N; i++)
{
if(!visit[i] && mark_graph[city][i])
{
dfs(i, op_index, mcmf);
}
}
}
int main()
{
scanf("%d", &T);
while(T--)
{
MCMF mcmf;
scanf("%d%d%d", &N, &M, &K);
int st = 0;
int ed = N + 1;
int max_fee = 551;
int index = ed;
int cnt_temp = 0;
mcmf._init(N + M + 2, (M * N + N + M) * 2, st, ed);
for(int i = 1; i <= N; i++)
{
mcmf.sadjlist.AddEdge(i, ed, 1, 0);
}
memset(mark_graph, false, sizeof(mark_graph));
for(int i = 1; i <= M; i++)
{
int op;
scanf("%d", &op);
switch(op)
{
case 1:
int city;
scanf("%d", &city);
memset(visit, false, sizeof(visit));
dfs(city, ++index, mcmf);
ans[cnt_temp++] = mcmf.sadjlist.cnt;
mcmf.sadjlist.AddEdge(st, index, K, --max_fee);
break;
case 2:
int u, v;
scanf("%d%d", &u, &v);
mark_graph[u][v] = true;
mark_graph[v][u] = true;
break;
case 3:
int num;
scanf("%d", &num);
while(num--)
{
int u, v;
scanf("%d%d", &u, &v);
mark_graph[u][v] = false;
mark_graph[v][u] = false;
}
break;
default:
break;
}
}
if(!cnt_temp)
{
printf("0\n");
continue;
}
printf("%d\n", mcmf.Get_MCMF());
for(int i = 0; i < cnt_temp; i++)
{
if(i) printf(" ");
printf("%d", mcmf.sadjlist.edge[ans[i] ^ 1].flow);
}
printf("\n");
}
return 0;
}