BZOJ1565: [NOI2009]植物大战僵尸
Description
Input
Output
仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。
Sample Input
3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
Sample Output
25
HINT
在样例中, 植物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 。
经典的最大权闭合子图,我们把依赖关系成环的节点以及依赖一个环的节点删掉,然后就是模板题。
然后我写了个Tarjan。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=Getchar(); for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1; for(;isdigit(c);c=Getchar()) x=x*10+c-'0'; return x*f; } const int maxn=610; const int maxm=1000010; const int inf=1e9; struct ISAP{ struct tedge{int x,y,w,next;}adj[maxm];int ms,fch[maxn]; int d[maxn],s[maxn],cur[maxn],gap[maxn],n,top; void init(int n){ this->n=n;ms=0;top=0; memset(d,-1,sizeof(d)); memset(fch,-1,sizeof(fch)); return; } void AddEdge(int u,int v,int w){ adj[ms]=(tedge){u,v,w,fch[u]};fch[u]=ms++; adj[ms]=(tedge){v,u,0,fch[v]};fch[v]=ms++; return; } void bfs(){ queue<int>Q;Q.push(n);d[n]=0; while(!Q.empty()){ int u=Q.front();Q.pop(); for(int i=fch[u];i!=-1;i=adj[i].next){ int v=adj[i].y; if(d[v]==-1) d[v]=d[u]+1,Q.push(v); } } return; } int solve(int S,int T){ n=T;bfs();int k=S,i,flow=0; for(i=0;i<=n;i++) cur[i]=fch[i],gap[d[i]]++; while(d[S]<n){ if(k==n){ int mi=inf,pos; for(i=0;i<top;i++) if(adj[s[i]].w<mi) mi=adj[s[i]].w,pos=i; for(i=0;i<top;i++) adj[s[i]].w-=mi,adj[s[i]^1].w+=mi; flow+=mi;top=pos;k=adj[s[top]].x; } for(i=cur[k];i!=-1;i=adj[i].next){ int v=adj[i].y; if(adj[i].w&&d[k]==d[v]+1){cur[k]=i;k=v;s[top++]=i;break;} } if(i==-1){ int lim=n; for(i=fch[k];i!=-1;i=adj[i].next){ int v=adj[i].y; if(adj[i].w&&d[v]<lim) lim=d[v],cur[k]=i; } if(--gap[d[k]]==0) break; d[k]=lim+1;gap[d[k]]++; if(k!=S) k=adj[s[--top]].x; } } return flow; } }sol; int n,m,val[maxn]; int id(int x,int y) {return (x-1)*m+y;} int from[maxm],to[maxm],first[maxn],next[maxm],e; void AddEdge(int u,int v) { from[++e]=u;to[e]=v;next[e]=first[u];first[u]=e; } int S[maxn],scn[maxn],cnt[maxn],top,num,pre[maxn],low[maxn],mark[maxn],dfs_clock; void dfs(int x) { low[x]=pre[x]=++dfs_clock;S[++top]=x; for(int i=first[x];i;i=next[i]) { if(!pre[to[i]]) dfs(to[i]),low[x]=min(low[x],low[to[i]]); else if(!scn[to[i]]) low[x]=min(low[x],pre[to[i]]); } if(low[x]==pre[x]) { num++;for(;;) { int u=S[top--]; scn[u]=num;cnt[num]++; if(u==x) break; } } } int main() { n=read();m=read(); rep(i,1,n) rep(j,1,m) { val[id(i,j)]=read(); int k=read(),u=id(i,j); while(k--) { int x=read()+1,y=read()+1; AddEdge(id(x,y),u); } if(j!=m) AddEdge(id(i,j),id(i,j+1)); } rep(i,1,n*m) if(!pre[i]) dfs(i); rep(i,1,n*m) if(cnt[scn[i]]>1) mark[i]=1; int S=n*m+1,T=n*m+2,sum=0;sol.init(T); rep(i,1,e) if(mark[to[i]]) sol.AddEdge(from[i],T,1e9); else sol.AddEdge(from[i],to[i],1e9); rep(i,1,n*m) if(!mark[i]) { if(val[i]>0) sum+=val[i],sol.AddEdge(S,i,val[i]); else sol.AddEdge(i,T,-val[i]); } printf("%d\n",sum-sol.solve(S,T)); return 0; }