[NOI2009 植物大战僵尸]

[关键字]:网络流

[题目大意]:每个植物都有一个分数(可正可负)和一个攻击位置集合(可为空集)。僵尸对植物发动进攻,只能从右向左进攻且不能走入被植物攻击到的位置,每消灭一个植物就可获得它的分数,求出最大得分(可为0,不发动进攻)。

//===================================================================================================

[分析]:就是一个最大权闭合子图的模型。首先由源点向所有正权点连一条容量为其权值的边,有所有负权点向汇连一条容量为-(权值)的边,然后由被保护点向保护点连一条∞的边。这样一来,求最小割S集就是所攻击到的点,用(sum(总正权点值)-最小割)就是答案。因为(1)、∞不可能被选入最小割,所以如果一个植物的分数在S集一定也会把保护他的植物选进S集,这样确保解是可行的。(2)、此时最小割的数值为sum(未选的正权点值)+sum(已选的负权点值)=(sum(总正权点值)-sum(已选正权点值))+sum(已选负权点值),所以sum(总正权点值)-最小割=sum(总正权点值)-[(sum(总正权点值)-sum(已选正权点值))+sum(已选负权点值)]=sum(已选正权点值)-sum(已选负权点值)=题目所求,又因为sum(总正权点值)不变,要想最后的值最大,就要让割最小即最小割。在本题中还有一个问题,就是有可能出现“无敌节点”——在构造的网络中出现环,此时环内所有节点都不可能取到,同时被环内节点保护的所有节点也不可取所以要先去掉这些点。方法是:把网络中所有边反向进行拓扑,拓扑到的点就是要保留的点。至于为什么反向,因为如果不反向那删掉的点是环内的点和保护环的点,但明显保护环的点是可以取到的,而真正因该去掉的被环保护的点却还存在,所以必须反向。

[代码]:

View Code
  1 #include<iostream>
2 #include<cstdio>
3 #include<cstdlib>
4 #include<cstring>
5 #include<algorithm>
6 #include<queue>
7 #define INF 10000000
8 using namespace std;
9
10 struct edge
11 {
12 int y,d,next,op;
13 } e[1000000],e2[1000000];
14 int now=0,tot=0,tot2=0,n,m,s,t,N=0,sum=0;
15 int map[200][200],sc[1000],r[1000],first[1000],first2[1000];
16 int h[1000],num[1000];
17 bool v[1000];
18
19 int inss(int x,int y)
20 {
21 tot++;
22 e[tot].y=y;
23 e[tot].next=first[x];
24 first[x]=tot;
25 return 0;
26 }
27
28 int add(int x,int y,int d)
29 {
30 tot2++;
31 e2[tot2].y=y;
32 e2[tot2].d=d;
33 e2[tot2].next=first2[x];
34 e2[tot2].op=tot2+1;
35 first2[x]=tot2;
36 tot2++;
37 e2[tot2].y=x;
38 e2[tot2].d=0;
39 e2[tot2].next=first2[y];
40 e2[tot2].op=tot2-1;
41 first2[y]=tot2;
42 return 0;
43 }
44
45 int init()
46 {
47 scanf("%d%d",&n,&m);
48 for (int i=0;i<n;i++)
49 for (int j=0;j<m;j++) map[i][j]=++now;
50 for (int i=0;i<n;i++)
51 for (int j=0;j<m;j++)
52 {
53 int num,x,y;
54 scanf("%d%d",&sc[map[i][j]],&num);
55 for (int k=0;k<num;k++)
56 {
57 scanf("%d%d",&x,&y);
58 r[map[x][y]]++;
59 inss(map[i][j],map[x][y]);
60 }
61 }
62 for (int i=0;i<n;i++)
63 for (int j=0;j<m-1;j++)
64 {
65 r[map[i][j]]++;
66 inss(map[i][j+1],map[i][j]);
67 }
68 return 0;
69 }
70
71 int SSSP()
72 {
73 queue<int> q;
74 memset(v,0,sizeof(v));
75 for (int i=1;i<=now;i++)
76 if (r[i]==0)
77 {
78 q.push(i);
79 v[i]=1;
80 }
81 while (!q.empty())
82 {
83 int u=q.front();
84 q.pop();
85 for (int p=first[u];p;p=e[p].next)
86 {
87 r[e[p].y]--;
88 if (r[e[p].y]==0)
89 {q.push(e[p].y); v[e[p].y]=1;}
90 }
91 }
92 return 0;
93 }
94
95 int find(int u,int flow)
96 {
97 if (u==t) return flow;
98 int temp=flow,pos=N-1;
99 for (int p=first2[u];p;p=e2[p].next)
100 {
101 if (h[u]==h[e2[p].y]+1 && e2[p].d>0)
102 {
103 int f=find(e2[p].y,min(e2[p].d,temp));
104 temp-=f;
105 e2[p].d-=f;
106 e2[e2[p].op].d+=f;
107 if (temp==0 || h[s]==N) return flow-temp;
108 }
109 if (e2[p].d>0 && pos>h[e2[p].y]) pos=h[e2[p].y];
110 }
111 if (temp==flow)
112 {
113 num[h[u]]--;
114 if (num[h[u]]==0) h[s]=N;
115 else
116 {
117 h[u]=pos+1;
118 num[h[u]]++;
119 }
120 }
121 return flow-temp;
122 }
123
124 int solve()
125 {
126 s=0;
127 t=n*m+1;
128 for (int i=1;i<=now;i++)
129 {
130 if (v[i] && sc[i]>0) {add(s,i,sc[i]); sum+=sc[i];}
131 if (v[i] && sc[i]<0) add(i,t,-sc[i]);
132 }
133 for (int i=1;i<=now;i++)
134 if (v[i])
135 for (int p=first[i];p;p=e[p].next)
136 if (v[e[p].y])
137 add(e[p].y,i,INF);
138 for (int i=1;i<=now;i++) N++;
139 N+=2;
140 memset(h,0,sizeof(h));
141 memset(num,0,sizeof(num));
142 num[0]=N;
143 int ans=0;
144 while (h[s]<N) ans+=find(s,INF);
145 return ans;
146 }
147
148 int main()
149 {
150 freopen("pvz.in","r",stdin);
151 freopen("pvz.out","w",stdout);
152 init();
153 SSSP();
154 printf("%d",max(sum-solve(),0));
155 //system("pause");
156 fclose(stdin);fclose(stdout);
157 return 0;
158 }



你可能感兴趣的:([NOI2009 植物大战僵尸])