bzoj-1565 植物大战僵尸
2015-11-24 15:37
645 查看
题意:
给出一片n*m的草坪,上面每个点有一个植物;
现在由你来从最右面放出一些僵尸来进攻这些植物;
僵尸到一个植物面前的时候就可以吃掉这个植物,并且得到这个植物的的得分(可正可负);
每个植物可以攻击几个位置,并且僵尸只能从右面出发,也就是说在吃掉右面的植物之前不可能吃掉左面的;
僵尸是无限的,求最大得分;
n<=20,m<=30;
题解:
原来这个就是最大权闭合子图啊。。似乎以前没刷过的样子= =
总之先建图:
将每个植物向能保护它的植物连流量为无穷的边;
从源点向正得分植物连流量为得分的边;
从负得分植物向汇点连流量为得分相反数的边;
最后用所有正权值的和减去最小割就是了;
这样建图解决最大权闭合子图问题是正确的,感性上讨论一下各种边就差不多理解的了吧;
然而通常的最大权闭合子图都是拓扑图,在这个问题中可以有环存在;
所以有些点是永远也不可能取到的,这些点我们不要,也就是拓扑排序之后排不到的点;
然后建图就是正确的了,跑最大权闭合子图即可;
代码:
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 40
#define E 400000
using namespace std;
int next[E],to[E],flow[E],head[N*N],ce=1;
int dis[N*N],S,T,sum;
int val[N*N];
queue<int>q;
void add(int x,int y,int fl)
{
to[++ce]=y;
flow[ce]=fl;
next[ce]=head[x];
head[x]=ce;
to[++ce]=x;
flow[ce]=0;
next[ce]=head[y];
head[y]=ce;
}
bool BFS()
{
int x,i;
memset(dis,0,sizeof(dis));
dis[S]=1;
q.push(S);
while(!q.empty())
{
x=q.front(),q.pop();
for(i=head[x];i;i=next[i])
{
if(flow[i]&&!dis[to[i]])
{
dis[to[i]]=dis[x]+1;
q.push(to[i]);
}
}
}
return dis[T]!=0;
}
int dfs(int x,int lim)
{
if(x==T) return lim;
int ret=0,i,temp;
for(i=head[x];i;i=next[i])
{
if(flow[i]&&dis[to[i]]==dis[x]+1)
{
temp=dfs(to[i],min(flow[i],lim-ret));
ret+=temp;
flow[i]-=temp,flow[i^1]+=temp;
if(ret==lim)
return ret;
}
}
if(!ret) dis[x]=0;
return ret;
}
namespace Graph
{
int next[E],to[E],head[N*N],ce;
int in[N*N];
bool vis[N*N];
void add(int x,int y)
{
to[++ce]=y;
next[ce]=head[x];
head[x]=ce;
in[y]++;
}
void slove(int n,int m)
{
int i,x,tot=n*m;
for(x=0;x<tot;x++)
{
if(!in[x])
q.push(x);
}
while(!q.empty())
{
x=q.front(),q.pop();
vis[x]=1;
for(i=head[x];i;i=next[i])
{
in[to[i]]--;
if(!in[to[i]])
q.push(to[i]);
}
}
for(x=0;x<tot;x++)
{
if(!vis[x]) continue;
if(val[x]>=0)
::add(S,x,val[x]),sum+=val[x];
else
::add(x,T,-val[x]);
for(i=head[x];i;i=next[i])
{
if(vis[to[i]])
::add(to[i],x,0x3f3f3f3f);
}
}
}
}
int main()
{
int n,m,w,i,j,k,x,y,ans;
scanf("%d%d",&n,&m);
S=n*m,T=S+1;
for(i=0,sum=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(j) Graph::add(i*m+j,i*m+j-1);
scanf("%d%d",&val[i*m+j],&w);
for(k=1;k<=w;k++)
{
scanf("%d%d",&x,&y);
Graph::add(i*m+j,x*m+y);
}
}
}
Graph::slove(n,m);
ans=0;
while(BFS())
ans+=dfs(S,0x3f3f3f3f);
printf("%d\n",sum-ans);
return 0;
}
给出一片n*m的草坪,上面每个点有一个植物;
现在由你来从最右面放出一些僵尸来进攻这些植物;
僵尸到一个植物面前的时候就可以吃掉这个植物,并且得到这个植物的的得分(可正可负);
每个植物可以攻击几个位置,并且僵尸只能从右面出发,也就是说在吃掉右面的植物之前不可能吃掉左面的;
僵尸是无限的,求最大得分;
n<=20,m<=30;
题解:
原来这个就是最大权闭合子图啊。。似乎以前没刷过的样子= =
总之先建图:
将每个植物向能保护它的植物连流量为无穷的边;
从源点向正得分植物连流量为得分的边;
从负得分植物向汇点连流量为得分相反数的边;
最后用所有正权值的和减去最小割就是了;
这样建图解决最大权闭合子图问题是正确的,感性上讨论一下各种边就差不多理解的了吧;
然而通常的最大权闭合子图都是拓扑图,在这个问题中可以有环存在;
所以有些点是永远也不可能取到的,这些点我们不要,也就是拓扑排序之后排不到的点;
然后建图就是正确的了,跑最大权闭合子图即可;
代码:
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 40
#define E 400000
using namespace std;
int next[E],to[E],flow[E],head[N*N],ce=1;
int dis[N*N],S,T,sum;
int val[N*N];
queue<int>q;
void add(int x,int y,int fl)
{
to[++ce]=y;
flow[ce]=fl;
next[ce]=head[x];
head[x]=ce;
to[++ce]=x;
flow[ce]=0;
next[ce]=head[y];
head[y]=ce;
}
bool BFS()
{
int x,i;
memset(dis,0,sizeof(dis));
dis[S]=1;
q.push(S);
while(!q.empty())
{
x=q.front(),q.pop();
for(i=head[x];i;i=next[i])
{
if(flow[i]&&!dis[to[i]])
{
dis[to[i]]=dis[x]+1;
q.push(to[i]);
}
}
}
return dis[T]!=0;
}
int dfs(int x,int lim)
{
if(x==T) return lim;
int ret=0,i,temp;
for(i=head[x];i;i=next[i])
{
if(flow[i]&&dis[to[i]]==dis[x]+1)
{
temp=dfs(to[i],min(flow[i],lim-ret));
ret+=temp;
flow[i]-=temp,flow[i^1]+=temp;
if(ret==lim)
return ret;
}
}
if(!ret) dis[x]=0;
return ret;
}
namespace Graph
{
int next[E],to[E],head[N*N],ce;
int in[N*N];
bool vis[N*N];
void add(int x,int y)
{
to[++ce]=y;
next[ce]=head[x];
head[x]=ce;
in[y]++;
}
void slove(int n,int m)
{
int i,x,tot=n*m;
for(x=0;x<tot;x++)
{
if(!in[x])
q.push(x);
}
while(!q.empty())
{
x=q.front(),q.pop();
vis[x]=1;
for(i=head[x];i;i=next[i])
{
in[to[i]]--;
if(!in[to[i]])
q.push(to[i]);
}
}
for(x=0;x<tot;x++)
{
if(!vis[x]) continue;
if(val[x]>=0)
::add(S,x,val[x]),sum+=val[x];
else
::add(x,T,-val[x]);
for(i=head[x];i;i=next[i])
{
if(vis[to[i]])
::add(to[i],x,0x3f3f3f3f);
}
}
}
}
int main()
{
int n,m,w,i,j,k,x,y,ans;
scanf("%d%d",&n,&m);
S=n*m,T=S+1;
for(i=0,sum=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(j) Graph::add(i*m+j,i*m+j-1);
scanf("%d%d",&val[i*m+j],&w);
for(k=1;k<=w;k++)
{
scanf("%d%d",&x,&y);
Graph::add(i*m+j,x*m+y);
}
}
}
Graph::slove(n,m);
ans=0;
while(BFS())
ans+=dfs(S,0x3f3f3f3f);
printf("%d\n",sum-ans);
return 0;
}
相关文章推荐
- Poj2638 网络流+最短路+二分答案
- BZOJ3275 Number (最小割)
- [笔记] 网络流-最大流 POJ-1273\HDU-4240
- 上下界网络流初探
- Edmonds-Karp 最大流 hdu 1532 Drained Ditches
- 网络流_poj1273
- POJ 1273 Drainage Ditches 最大流 dinic
- POJ1273-Drainage Ditches
- 【网络流】复杂的大门
- ACM/ICPC World Finals 2013 C Surely You Congest
- [bzoj1003] [ZJOI2006]物流运输trans
- 网络流算法整理
- [bzoj1500][NOI2005]维修数列
- [bzoj1208] [HNOI2004]宠物收养所
- [bzoj1269][AHOI2006]文本编辑器editort
- [bzoj1503][NOI2004]郁闷的出纳员
- [BZOJ2038][2009国家集训队][莫队][分块]小z的袜子
- [BZOJ2594][WC2006][LCT][MST]水管局长数据加强版
- [BZOJ2300][HAOI2011][动态凸包]防线修建
- BZOJ - 1609/USACO - Feb08 Silver Eating Together 麻烦的聚餐 重庆一中高2018级竞赛班第九次测试 2016.9.10 Problem 2