点这里 |
---|
题意: 给定一个带权有向图,n个城市m条路(有边权),以及f个赠与你的城市(带有点权)。你需要从受赠与的城市中选择一些(可全部),并割断某些边将这些城市与其他城市隔开,你的最终受益等于所有受赠的点权和减去割掉的边权和。计算你的最大收益。并且输出割掉的边的编号。
题解: 要注意我们的收益是受赠与的城市的权值和减去割掉的边权和,而且必须要将你选择的城市与其他城市分割开。因此我们需要计算出能分割你的城市和其他的城市的最小边权和,而这个最小值,就是最大流的值。
至于要打印割边的编号,那我们要清楚就算计算出了最大流,我们要舍弃哪些边。将你选择的城市看作一个连通分量T,将剩余的其他城市看作一割连通分量S,我们割的边,需要能连接这两个分量。
具体步骤:
- 计算最大流: 这个只要用Dinic的模板就能计算,唯一要变通的就是设置一个超级汇点t = n + 1,将受赠的城市与超级汇点连接。
- 分割: 利用Dinic中的残留容量图,对源点进行dfs,将所有残留容量不为零的边所连接的点,统统归入S的集合。如此一来,集合内的点即为连通分量S,而连接不到的点即为连通分量T。
- 输出割边: 但是要注意!不是所有连接两个连通分量的边就一定是我们需要割的边。因为我们在图中加入了一个超级汇点t,所以我们还需要舍弃所以连接到超级汇点的边。
过程中犯的错:
- init(): 我将超级汇点的设置放在了init函数中,而
t = n + 1
,必须在读入n之后才能进行初始化。 - 割边需要舍弃连接超级汇点的边: 前面解释过原因。
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 1e3 + 10;
const int M = 2e5 + 10;
int T, n, m, f, k;
int s, t, cnt, sum;
int vis[N], cur[N], head[N], depth[N];
struct edge{ int v, w, next;} e[M];
void init(){ s = 1, t = n + 1, cnt = sum = 0; for(int i = 0; i < N; i++) vis[i] = 0, head[i] = -1;}
void add(int u, int v, int w){ e[cnt] = edge{v, w, head[u]};head[u] = cnt++;}
void addedge(int u, int v, int w){ add(u, v, w); add(v, u, 0);}
bool bfs(){
memset(depth, 0, sizeof depth);
queue<int> Q;
Q.push(s); depth[s] = 1;
while(!Q.empty()){
int u = Q.front(); Q.pop();
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].v, w = e[i].w;
if(depth[v] == 0 && w > 0){
depth[v] = depth[u] + 1;
Q.push(v);
}
}
}
if(depth[t] > 0) return 1;
return 0;
}
int dfs(int u, int flow){
if(u == t) return flow;
for(int& i = cur[u]; ~i; i = e[i].next){
int v = e[i].v, w = e[i].w;
if(depth[v] == depth[u] + 1 && w != 0){
int minflow = dfs(v, min(flow, w));
if(minflow > 0){
e[i].w -= minflow;
e[i ^ 1].w += minflow;
return minflow;
}
}
}
return 0;
}
int dinic(){
int ans = 0;
while(bfs()){
for(int i = 1; i < N; i++) cur[i] = head[i];
while(int d = dfs(s, inf)) ans += d;
}
return ans;
}
void dfs(int u){
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].v;
if(!vis[v] && e[i].w > 0) vis[v] = 1, dfs(v);
}
}
void print_cut(){
vis[1] = 1; dfs(1);
queue<int> Q;
for(int i = 0; i < cnt; i += 2){
int v = e[i].v, u = e[i ^ 1].v;
if(vis[u] && !vis[v] && v != t) Q.push(i);
}
printf("%d", Q.size());
while(!Q.empty()){
int u = Q.front(); Q.pop();
printf(" %d", u / 2 + 1);
}
printf("\n");
}
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &m, &f);
init(); //要在读入之后!
while(m--){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
while(f--){
int u, w;
scanf("%d%d", &u, &w);
addedge(u, t, w);
sum += w;
}
int ans = dinic();
printf("Case %d: %d\n", ++k, sum - ans);
print_cut();
}
return 0;
}