题目:BZOJ1565.
题目大意:给定一张
n
∗
m
n*m
n∗m的网格,坐标从
(
0
,
0
)
(0,0)
(0,0)到
(
n
−
1
,
m
−
1
)
(n-1,m-1)
(n−1,m−1),规定若一个网格
(
x
,
y
)
(x,y)
(x,y)要取,则
(
x
,
y
+
1
)
,
(
x
,
y
+
2
)
,
.
.
.
(
x
,
m
−
1
)
(x,y+1),(x,y+2),...(x,m-1)
(x,y+1),(x,y+2),...(x,m−1)都要取.现在给定每个网格
(
i
,
j
)
(i,j)
(i,j)的价值
v
(
i
,
j
)
v(i,j)
v(i,j),以及每个网格
(
i
,
j
)
(i,j)
(i,j)是哪些网格
(
i
′
,
j
′
)
(i',j')
(i′,j′)的必选项(即要选择
(
i
′
,
j
′
)
(i',j')
(i′,j′),必须先选择
(
i
,
j
)
(i,j)
(i,j)),求可以获得的最大价值和.
1
≤
n
≤
20
,
1
≤
m
≤
30
,
−
1
0
4
≤
v
(
i
,
j
)
≤
1
0
4
1\leq n\leq 20,1\leq m\leq 30,-10^4\leq v(i,j)\leq 10^4
1≤n≤20,1≤m≤30,−104≤v(i,j)≤104.
看到条件比较复杂而且数据范围比较奇怪的题,容易想到网络流来做.
看到条件是选了某个点就必须要选某些点的题,容易想到是最大权闭合子图.
然而不幸的是,这道题的限制条件可能形成一个环,而用最大权闭合子图做的题需要原图是个DAG.
考虑环怎么处理,显然一个环上的任意一个点都是不可能取的,而任意一个直接或间接以环作为前置条件的点也是不可取的,所以想到了SCC缩点,把所以包含点数大于 2 2 2的SCC以及所有能够通向它们的点都去掉,剩下的图就是一个DAG了.
之后跑个最大权闭合子图就好了.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=600,M=N*(N+2),INF=(1<<30)-1;
int n,m,st,td,v[N+9],sum;
int H(int x,int y){return (x-1)*m+y;}
struct side{
int y,next,f;
}e[M*4+9];
int lin[3][N+9],cs;
void Ins(int id,int x,int y,int v){e[++cs].y=y;e[cs].f=v;e[cs].next=lin[id][x];lin[id][x]=cs;}
int dfn[N+9],low[N+9],vis[N+9],co;
stack<int>sta;
int bel[N+9],cnt,cn[N+9];
vector<int>scc[N+9];
void Tarjan(int k){
dfn[k]=low[k]=++co;
vis[k]=1;sta.push(k);
for (int i=lin[0][k];i;i=e[i].next)
if (!dfn[e[i].y]) Tarjan(e[i].y),low[k]=min(low[k],low[e[i].y]);
else if (vis[e[i].y]) low[k]=min(low[k],low[e[i].y]);
if (dfn[k]^low[k]) return;
++cnt;
while (vis[k]){
int t=sta.top();sta.pop();
vis[t]=0;
scc[bel[t]=cnt].push_back(t);
++cn[cnt];
}
}
int b[N+9];
void Contract(){
for (int i=1;i<=n;++i)
if (!dfn[i]) Tarjan(i);
for (int i=1;i<=n;++i)
for (int j=lin[0][i];j;j=e[j].next)
if (bel[i]^bel[e[j].y]) Ins(1,bel[i],bel[e[j].y],0);
for (int i=1;i<=cnt;++i)
if (cn[i]>1) b[i]=1;
}
void Dfs_b(int k){
if (vis[k]) return;
vis[k]=1;
for (int i=lin[1][k];i;i=e[i].next){
Dfs_b(e[i].y);
b[k]|=b[e[i].y];
}
}
int dis[N+9],gap[N+9];
queue<int>q;
void Bfs_div(int st,int td){
gap[dis[td]=1]=1;q.push(td);
while (!q.empty()){
int t=q.front();q.pop();
for (int i=lin[2][t];i;i=e[i].next)
if (!dis[e[i].y]){
++gap[dis[e[i].y]=dis[t]+1];
q.push(e[i].y);
}
}
}
int cur[N+9];
int Dfs_path(int k,int st,int td,int flow){
if (k==td) return flow;
int res=0;
for (int &i=cur[k];i;i=e[i].next)
if (dis[k]==dis[e[i].y]+1){
int t=Dfs_path(e[i].y,st,td,min(flow,e[i].f));
flow-=t;res+=t;
e[i].f-=t;e[i^1].f+=t;
if (!flow) return res;
}
if (!(--gap[dis[k]])) dis[st]=n+1;
++gap[++dis[k]];
cur[k]=lin[2][k];
return res;
}
int Max_flow(int st,int td){
int res=0;
Bfs_div(st,td);
for (int i=1;i<=n;++i) cur[i]=lin[2][i];
for (;dis[st]<=n;res+=Dfs_path(st,st,td,INF));
return res;
}
Abigail into(){
scanf("%d%d",&n,&m);
cs=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j){
for (int k=j+1;k<=m;++k) Ins(0,H(i,j),H(i,k),0);
int sk,x,y;
scanf("%d%d",&v[H(i,j)],&sk);
for (int k=1;k<=sk;++k){
scanf("%d%d",&x,&y);++x;++y;
Ins(0,H(x,y),H(i,j),0);
}
}
n*=m;
}
Abigail work(){
Contract();
for (int i=1;i<=cnt;++i){
Dfs_b(i);
if (!b[i]) continue;
for (int j=0;j<cn[cnt];++j) v[scc[i][j]]=0;
}
cs|=1; //注意这里一定要按位或上1,不然网络流中找反向边的异或1就炸了...
for (int i=1;i<=n;++i)
for (int j=lin[0][i];j;j=e[j].next)
Ins(2,i,e[j].y,INF),Ins(2,e[j].y,i,0);
st=n+1;td=n+2;
for (int i=1;i<=n;++i)
if (v[i]>=0) sum+=v[i],Ins(2,st,i,v[i]),Ins(2,i,st,0);
else Ins(2,i,td,-v[i]),Ins(2,td,i,0);
n+=2;
}
Abigail outo(){
printf("%d\n",sum-Max_flow(st,td));
}
int main(){
into();
work();
outo();
return 0;
}