因为昨天做了一道最大获利,所以感觉思路差不多
对于每个植物,首先思考如果它们score都是整数的时候,贪心就好了,可以到达的就攻击
然而植物的score有负数,所以植物就可以分为获得收益的和付出代价的
这样大体思路就是分为两部分,代价连源点,收益连汇点,中间有保护关系的连不可删边,再跑个最小割
然而这个题的细节还是让我这个菜鸡一遍爆0
首先,显而易见的保护关系是攻击型的植物对攻击位置的保护关系
但是还有一些保护关系,例如想要攻击左边的植物就必须除掉右边的植物,所以还有这层隐藏保护关系
所以这些保护关系就能构成一个图,但这个图并不是一个DAG,环上的点以及环上的点都是不可除掉的点,这些点必须处理出来,不能放到网络流图中(样例还是比较良心的,能让人发现必须处理这种情况)
还有对于这类互相保护求最大收益跑最小割的问题,作为一个菜鸡我理解的也不是很透,但是可以发现,例如原图中关系为a保护b,b保护c,如果不直接处理成直接关系,从a向c连边,是会出问题的(这样的话边数会比较多,然而看一看数据范围这么小,NOI的题数据范围这么小肯定是有原因的啊)
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#define INF ((int)1e9)
#define fr(i,s,t) for(i=s;i<=t;i++)
using namespace std;
struct edge{
int to,cap,flow;
};
vector <edge> edges;
vector <int> vec1[3000],vec[3000];
int a[101][101],n,m,degree[3000],N,T,m1,ans,d[3000],cur[3000];
bool f[3000],vis[3000],dis[601][601];
int read(){
int x=0,w=1; char ch=0;
while (ch<'0'||ch>'9'){
w=(ch=='-')?-1:1;
ch=getchar();
}
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w*x;
}
void init(){
n=read(); m=read();
int i,j,k,num,x,y;
fr(i,1,n) fr(j,1,m){
N++;
fr(k,1,m) if (j-k>=1) vec1[N].push_back(N-k);
a[i][j]=read(); num=read();
fr(k,1,num){
x=read(); y=read();
x++; y++;
vec1[N].push_back((x-1)*m+y);
}
}
}//建保护关系的原图
void dfs(int x){
vis[x]=1;
int i,y;
for (i=0;i<vec1[x].size();i++){
y=vec1[x][i];
if (vis[y]) continue;
dfs(y);
}
}
void work(){
int i,j;
fr(i,1,N){
memset(vis,0,sizeof(vis));
dfs(i);
fr(j,1,N) if (vis[j]&&i!=j)
dis[i][j]=1,degree[j]++;
}
}//处理保护关系
void topo(){//处理不可达点
int i,x,y,j;
work();
memset(f,1,sizeof(f));
queue <int> Q;
fr(i,1,N)
if (!degree[i])
f[i]=0,Q.push(i);
while (!Q.empty()){
x=Q.front(); Q.pop();
for (y=1;y<=N;y++){
if (!dis[x][y]) continue;
if (!f[y]) continue;
degree[y]--;
if (!degree[y]) Q.push(y),f[y]=0;
}
}
fr(i,1,N) if (f[i])
fr(j,1,N) if (dis[i][j])
f[j]=1;
}
//处理保护关系原图
void addedge(int x,int y,int cap){
edges.push_back((edge){y,cap,0});
edges.push_back((edge){x,0,0});
m1+=2;
vec[x].push_back(m1-2);//mdzzm1写成m了
vec[y].push_back(m1-1);
}
void build(){
int i,j,num=0,y;
fr(i,1,N)
if (!f[i])
if (a[(i-1)/m+1][(i-1)%m+1]>0)
ans+=a[(i-1)/m+1][(i-1)%m+1];
T=2*N+1; m1=0;
fr(i,1,n) fr(j,1,m){
num++;
if (f[num]) continue;
if (a[i][j]>0) addedge(N+num,T,a[i][j]);
if (a[i][j]<0) addedge(0,num,-a[i][j]);
}
fr(i,1,N)
if (!f[i])
for (y=1;y<=N;y++){
if (f[y]) continue;
if (!dis[i][y]) continue;
addedge(i,y+N,INF);
}
}//建网络流图
bool bfs(){
memset(d,0,sizeof(d));
queue <int> Q;
Q.push(0);
int x,i,y,num;
while (!Q.empty()){
x=Q.front(); Q.pop();
for (i=0;i<vec[x].size();i++){
num=vec[x][i];
if (edges[num].cap==edges[num].flow) continue;
y=edges[num].to;
if (y&&!d[y]){
d[y]=d[x]+1;
Q.push(y);
}
}
}
return d[T];
}
int dfs(int x,int a){
if (x==T||a==0) return a;
int num,y,flow=0,f;
for (;cur[x]<vec[x].size();cur[x]++){
num=vec[x][cur[x]];
y=edges[num].to;
if (d[x]+1==d[y]&&(f=dfs(y,min(a,edges[num].cap-edges[num].flow)))>0){
edges[num].flow+=f;
edges[num^1].flow-=f;
flow+=f;
a-=f;
if (a==0) break;
}
}
return flow;
}
int main(){
init();
topo();
build();
while (bfs())
memset(cur,0,sizeof(cur)),ans-=dfs(0,INF);
cout<<ans;
}