题目
在样例中, 植物P1,1可以攻击位置(0,0), P2, 0可以攻击位置(2,1)。
一个方案为,首先进攻P1,1, P0,1,此时可以攻击P0,0 。共得到能源收益为(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保护,所以无法攻击第2行中的任何植物。
【大致数据规模】
约20%的数据满足1 ≤ N, M ≤ 5;
约40%的数据满足1 ≤ N, M ≤ 10;
约100%的数据满足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。
题解
这道题是最大权闭合子图,求一个最后没有出度的子图。用网络流来做,将所有点之间的边流量为INF,点权为正的连原点,流量为点权,点权为负的连汇点,流量为绝对值。答案为所有正点权和-最大流。但是这道题目还有环保护的问题,一旦环保护了,那么都不可以取,跑最大流会坑!我们要用拓扑排序来处理,最后不断的去掉入度为0的点,得到新的入度为0的点,一些点入度总是不能为0说明在环内,要去掉。这样之后跑最大流就好!【我做的时候拓扑排序思路不是太清晰QAQ
代码
/*Author:WNJXYK*/ #include<cstdio> #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<queue> #include<set> #include<map> using namespace std; #define LL long long #define Inf 2147483647 #define InfL 10000000000LL inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;} inline void swap(LL &x,LL &y){LL tmp=x;x=y;y=tmp;} inline int remin(int a,int b){if (a<b) return a;return b;} inline int remax(int a,int b){if (a>b) return a;return b;} inline LL remin(LL a,LL b){if (a<b) return a;return b;} inline LL remax(LL a,LL b){if (a>b) return a;return b;} const int Maxn=1000; const int Maxm=500000; int Ans=0; struct Edge{ int v,f,nxt; Edge(){} Edge(int a,int b,int c){v=a;f=b;nxt=c;} }; Edge e[Maxm+10]; int head[Maxn+10]; int nume=1; inline void addEdge(int u,int v,int f){ e[++nume]=Edge(v,f,head[u]); head[u]=nume; e[++nume]=Edge(u,0,head[v]); head[v]=nume; //printf("MakeEdge %d -> %d : %d\n",u,v,f); } int src; int sink; queue<int> que; int dist[Maxn+10]; inline bool bfs(){ while(!que.empty()) que.pop(); memset(dist,-1,sizeof(dist)); que.push(src); dist[src]=0; while(!que.empty()){ int now=que.front(); //printf("BFS->%d\n",now); que.pop(); for (int i=head[now];i;i=e[i].nxt){ //printf("BFS Try->%d\n",e[i].v); int tp=e[i].v; if (dist[tp]==-1 && e[i].f!=0){ dist[tp]=dist[now]+1; que.push(tp); } } } return dist[sink]!=-1; } int dfs(int x,int delta){ int ret=0; if (x==sink){ return delta; }else{ for (int i=head[x];i;i=e[i].nxt){ int tp=e[i].v; if (dist[tp]==dist[x]+1 && e[i].f!=0){ int dd=dfs(tp,remin(delta,e[i].f)); e[i].f-=dd; e[i^1].f+=dd; delta-=dd; ret+=dd; } } } return ret; } inline int dinic(){ int ret=0; while(bfs())ret+=dfs(src,Inf); return ret; } int n,m; int score[35][35]; struct MapEdge{ int x,y; int nxt; MapEdge(){} MapEdge(int a,int b,int c){x=a;y=b;nxt=c;} }; MapEdge me[Maxm+10]; int MapNume=0; int MapHead[35][35]; int inpoint[35][35]; bool visited[35][35]; inline void addMapEdge(int x,int y,int x1,int y1){ me[++MapNume]=MapEdge(x1,y1,MapHead[x][y]); MapHead[x][y]=MapNume; inpoint[x1][y1]++; } inline int point2id(int x,int y){ return (x-1)*m+y+1; } queue<pair<int,int> > MapQue; inline void spfaR(int x,int y){ //memset(visited,false,sizeof(visited)); while(!MapQue.empty()) MapQue.pop(); visited[x][y]=true; MapQue.push(make_pair(x,y)); while(!MapQue.empty()){ int nowx=MapQue.front().first,nowy=MapQue.front().second; //printf("TopSort->%d %d\n",nowx,nowy); MapQue.pop(); for (int i=MapHead[nowx][nowy];i;i=me[i].nxt){ int tx=me[i].x,ty=me[i].y; if (inpoint[tx][ty]<=1){ addEdge(point2id(tx,ty),point2id(nowx,nowy),Inf); inpoint[tx][ty]--; visited[tx][ty]=true; MapQue.push(make_pair(tx,ty)); }else{ addEdge(point2id(tx,ty),point2id(nowx,nowy),Inf); inpoint[tx][ty]--; } } } } inline void solveRoundP(){ for (int i=1;i<=n;i++){ for (int j=1;j<=m;j++){ if (inpoint[i][j]<=0 && visited[i][j]==false){ spfaR(i,j); } } } for (int i=1;i<=n;i++){ for (int j=1;j<=m;j++){ if (inpoint[i][j]<=0){ if (score[i][j]>=0){ addEdge(src,point2id(i,j),score[i][j]); Ans+=score[i][j]; }else{ addEdge(point2id(i,j),sink,-score[i][j]); } } ///////Arrest //if (inpoint[i][j]==0) //printf("UsePoint->%d %d\n",i,j); } } } int main(){ //freopen("pvz.in","r",stdin); //freopen("pvz.out","w",stdout); scanf("%d%d",&n,&m); src=1; sink=n*m+2; for (int i=1;i<=n;i++){ for (int j=1;j<=m;j++){ if (j>1) addMapEdge(i,j,i,j-1); scanf("%d",&score[i][j]); int w; scanf("%d",&w); for (int k=1;k<=w;k++){ int x,y; scanf("%d%d",&x,&y); x++;y++; if (x!=i || y!=j-1) addMapEdge(i,j,x,y); } } } solveRoundP(); printf("%d\n",Ans-dinic()); return 0; }