码风略毒瘤
序
我怕不是疯了
正文
这题花了我3h。其实想法有了就好想,可惜,我没有想法。
其实,一开始,做这个题我想了贪心和dp的方法,但是,因为一个很神奇的植物的保护范围,所以我把最后的尝试,放在了图论上
开始时,其实我也不知道怎么建边,
because 僵尸只能一步步的进攻
- 建一个边,从每一行的前一个向后一个建边,表示一种 protect 关系
then 植物有攻击范围
- 建一个边,从保护人向受保护人建边,表示一种 protect 关系
注意,这里的 a -> b 表示 a 保护 b
然后,我想到了是否能用 tarjan 或者 topo 来求个环,然后这些植物就是无敌的,就不考虑了。
当然,topo 的时候是考虑入度就行了,然而在我想完 topo 之后,我束手无策了。咋办,凉拌,不可做,走人(选择 topo 的原因是码量小,而且会比 topo 快,当然事实上 topo 还有更重要的一些优点)
然后我的想法是,类似于接着做 topo,但是,发现没有最优子结构之后果断放弃。再想想是否可以树形dp(话说这是一个名词),就像 CF467D 它那题也是跑了一个 tarjan ,之后发现状态无法很好的定义,所以。。凉凉。
我几乎放弃了图论的算法,准备写一个暴力,开阔思路。
暴力思想如下:
- 枚举放僵尸的过程
- 终止条件是没有路能放僵尸
- 注意:这是个回溯算法!
- 时间复杂度是 (O(2^{nm^{m}})),哈哈
崩了,崩了!
丝毫没有起到开阔思路的操作,还增添了我对暴力的好感。。我又回归了图论
ps:注意我现在操作的是反向图即 (forall (u,v) in E) 则有 v 受 u 的保护
我们知道,如果选了一个点,那么我一定要选出它的后继,所以,我陷入了 topo 的遐想,在之后崩了。
我点开算法标签 最大流,???黑人问号,最大闭合子图,听都没听说过。于是我 baidu 了下然后,看了一下最大闭合子图的一些文章,真是豁然开朗!
这个算法刚好能够解决我的问题,我们只需要建一个反图,然后这就满足了一个 $forall u in V' $ 且有 ((u,v) in E) 则必有 (v in E)的性质(这就是为啥叫闭合了)
献上代码
/* make by ltao */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <set>
#include <list>
#include <map>
#include <time.h>
#include <fstream>
typedef unsigned long long ull;
typedef double db;
typedef long long ll;
#define fake int
#define sz 666666
#define inf sz
#define get() getchar()
using namespace std;
string read_str(){
string x;
char ch=get();
while(ch=='
'||ch=='
'||ch==' ') ch=get();
while(ch!='
'&&ch=='
'&&ch!=' ')x+=ch,ch=get();
return x;
}
char read_ch(){
char ch=get();
while(ch=='
'||ch=='
'||ch==' ') ch=get();
return ch;
}
int read(){
int x=0;bool f=0;
char ch=get();
while(ch<'0'||ch>'9'){
if(ch=='-') f=1;
ch=get();
}
while(ch<='9'&&ch>='0'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=get();
}
return f?-x:x;
}
const int Maxm=2*1e5+11,Maxn=2002;
int n,m,s,t,h[Maxn],cnt,maxflow,sco[Maxn],w,x,y,in[Maxn],sum,dep[Maxn],cur[Maxn];
bool can[Maxn];
struct Edge{
int fr,to,lac,flow;
void insert(int x,int y,int z){
fr=x;to=y;
lac=h[x];
h[x]=cnt++;
flow=z;
}
}edge[Maxm],vedge[Maxm];
bool bfs(int s,int t){
memset(dep,-1,sizeof dep);
memcpy(cur,h,sizeof cur);
queue<int> q;
q.push(s);dep[s]=0;
while(!q.empty()){
int fr=q.front();
q.pop();
for(int i=h[fr];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if(!edge[i].flow||dep[to]!=-1) continue;
dep[to]=dep[fr]+1;
q.push(to);
if(to==t){
while(!q.empty()) q.pop();
return 1;
}
}
}
return 0;
}
int dfs(int u,int min1){
if(u==t) return min1;
int sum=min1;
for(int i=cur[u];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if(!edge[i].flow||dep[to]!=dep[u]+1) continue;
int ret=dfs(to,min(sum,edge[i].flow));
cur[u]=i;
edge[i].flow-=ret,edge[i^1].flow+=ret,sum-=ret;
if(sum==0) break;
}
return min1-sum;
}
void Dinic(int s,int t){
while(bfs(s,t))
maxflow+=dfs(s,0x3f3f3f3f);
}
int main() {
n=read();m=read();s=0;t=n*m+1;
memset(h,-1,sizeof h);
for(int i=1;i<=n*m;i++){
sco[i]=read();
w=read();
while(w--){
x=read();y=read();
edge[cnt].insert(i,x*m+y+1,0);
in[x*m+y+1]++;
}
//做他保护的
if(i%m!=1){
edge[cnt].insert(i,i-1,0);
in[i-1]++;
}
//连接之前的
}
//topo
queue <int> q;
for(int i=m;i<=n*m;i+=m)
if(in[i]==0){
q.push(i);
}
while(!q.empty()){
int fr=q.front();q.pop();
can[fr]=1;
for(int i=h[fr];i!=-1;i=edge[i].lac){
int to=edge[i].to;
in[to]--;
if(!in[to]) q.push(to);
}
}
//做topo
int vcnt=cnt;cnt=0;
memset(h,-1,sizeof h);
memcpy(vedge,edge,sizeof vedge);
for(int i=0;i<vcnt;i++){
int to=vedge[i].to,fr=vedge[i].fr;
if(can[fr]&&can[to]){
//由于最大权闭合子图
//我们 to 收到 fr 的保护
//所以有 to 必有 fr
edge[cnt].insert(to,fr,inf);
edge[cnt].insert(fr,to,0);
}
}
//连接汇点和源点
for(int i=1;i<=n*m;i++)
if(can[i]){
if(sco[i]>0){
edge[cnt].insert(s,i,sco[i]);
edge[cnt].insert(i,s,0);
sum+=sco[i];
}
if(sco[i]<0){
edge[cnt].insert(i,t,-sco[i]);
edge[cnt].insert(t,i,0);
}
}
Dinic(s,t);
printf("%d",sum-maxflow);
return 0;
}
主要的思想是 topo+Dinic 就好了
其实,自己意会一下就可以知道那个算法的正确性
嵬
是的,我就是疯了
我怎么会跟qs聊天。。
跟她一起,我真是越来越不正常了