题意描述:
一个国家有N个城市,标号1~N,初始时城市和道路都被破坏,下面进行以下三个操作:
第一种操作:1 X
修建城市,和X(包括X自己也可以被修建)直接相连或间接相连的城市可以被一次性修建(但一次性最多修建k个城市)
第二种操作:2 X Y
在X 与 Y之间建立一条道路
第三种操作:3 p X1 Y1 X2 Y2 ···
表示破坏X1与Y1、X2与Y2之间的道路
经过M次操作后,对于这M次操作中的操作一,我们在每次操作一时修建多少城市修建那些城市才能使最终城市数量最多?
并输出最终修建城市的总数量,以及每次操作一时修建的城市的数量,如果有多重情况,输出字典序最小的情况
思路:
初步分析感觉每次修建多少城市修建那些城市无从下手,仔细分析会发现本题的最终目的是:
最多可以选多少城市(最大匹配或最大流),每次一次性组多修建K个城市(容量上限为K),按字典序最小输出(通过费用控制)
显然这是一道:最小费用最大流问题
如下建图:
1、找两个标号作为源点(0)和汇点(n+1)
2、从1~n各城市分别和汇点(n+1)建边,容量为1,费用为0
3、对于每个操作一作为一个结点(从n+2开始编号),从源点到操作一结点建边,容量为K费用为cost=m+10,每次cost自减一即可
4、从操作一结点到1~n城市建边,如果此刻操作一和某些城市之间或间接相连,则建边,容量为1费用为0
源代码:
#include <cstdio>
#include <cstring>
#include <queue>
#define MAXN 1000
#define MAXE 110000
#define INF 0x3f3f3f3f
using namespace std;
int g[210][210];
bool flag[210];
struct Edge{
int to,next,cap,flow,cost;
}edge[MAXE*2];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;
int n,m,k;
int index;
int ans[510];
int as;
void addEdge(int u,int v,int cap,int cost){
edge[tol].to=v;
edge[tol].cap=cap;
edge[tol].cost=cost;
edge[tol].flow=0;
edge[tol].next=head[u];
head[u]=tol++;
edge[tol].to=u;
edge[tol].cap=0;
edge[tol].cost=-cost;
edge[tol].flow=0;
edge[tol].next=head[v];
head[v]=tol++;
}
bool spfa(int s,int t){
queue<int> q;
for(int i=0;i<N;++i){
dis[i]=INF;
vis[i]=false;
pre[i]=-1;
}
dis[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty()){
int u = q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
if(pre[t]==-1) return false;
else return true;
}
int minCostMaxflow(int s,int t,int &cost){
int flow=0;
cost=0;
while(spfa(s,t)){
int Min=INF;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
if(Min>edge[i].cap-edge[i].flow)
Min=edge[i].cap-edge[i].flow;
}
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
edge[i].flow+=Min;
edge[i^1].flow-=Min;
cost+=edge[i].cost*Min;
}
flow+=Min;
}
return flow;
}
void dfs(int root,int par){
addEdge(par,root,1,0);
flag[root]=true;
for(int i=1;i<=n;++i){
if(g[root][i]&&!flag[i])
dfs(i,par);
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&k);
index=n+2;
tol=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;++i){
addEdge(i,n+1,1,0);
}
memset(g,0,sizeof(g));
int MAXcost=510;
as=0;
for(int i=1;i<=m;++i){
int op,p,x,y;
scanf("%d",&op);
if(op==1){
memset(flag,false,sizeof(flag));
scanf("%d",&p);
dfs(p,index);
ans[as]=tol;
as++;
addEdge(0,index,k,MAXcost);
MAXcost--;
index++;
}
else if(op==2){
scanf("%d%d",&x,&y);
g[x][y]=1;
g[y][x]=1;
}
else if(op==3){
scanf("%d",&p);
for(int i=1;i<=p;i++){
scanf("%d%d",&x,&y);
g[x][y]=0;
g[y][x]=0;
}
}
}
N=index;
int cost;
if(as==0){
printf("0\n");
continue;
}
printf("%d\n",minCostMaxflow(0,n+1,cost));
for(int i=0;i<as-1;i++)
printf("%d ",edge[ans[i]].flow);
printf("%d\n",edge[ans[as-1]].flow);
}
return 0;
}