题目描述
小明最近在玩一款很好玩的游戏,游戏规则是这样的:
有一个n∗mn*mn∗m的地图,地图上的每一个位置要么是空地,要么是炮塔,要么是一些敌人,小明需要操纵炮塔攻击敌人。
攻击方法是:对于每个炮塔,游戏系统已经给出它可以瞄准的方向(上、下、左、右其中一个),小明需要选择它的攻击位置,每一个炮塔只能够攻击一个位置,炮塔只能够向着它的瞄准方向上的某个位置发动攻击,当然炮塔也可以不进行攻击。炮塔威力强大,它可以且仅可以消灭目标位置上所有敌人。
出于安全考虑,游戏系统已经保证不存在一个炮塔能够瞄准另外一个炮塔,即对于任意一个炮塔,它所有可能的攻击位置上不存在另外一个炮塔。而且,如果把炮塔的起点和终点称为炮弹的运行轨迹,那么系统不允许两条轨迹相交(包括起点和终点)。
现在,选定目标位置以后,每一个炮塔同时开炮,你要告诉小明,他最多可以干掉多少敌人。
解析
这题的关键还是在建图上
先求出每个炮塔能打到的最大值,加在一起设为tot
然后考虑冲突的情况
由于炮塔互相达不到,因此冲突一定是横向与竖向的冲突
把每个点割成横点和竖点两个点,横点向竖点连一条inf的边
然后对与每个横向炮塔,连一条s到炮塔的inf的边
然后从这个炮塔一直往炮弹方向相邻地连边,边权为 (炮塔达到的最大值) - (边的起点格子的权值)
竖向炮塔同理,连向t点,边的方向全反过来balabala…
这样不合法的情况就会使s与t联通
考虑割掉一条(i,j)(i,j)(i,j)到(i,j+1)(i,j+1)(i,j+1)的边,含义就是对应的炮塔改为攻击(i,j)(i,j)(i,j)的敌人,并损失相应的代价
最后的答案就是 tot-最小割
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5050;
const int M=1e9;
ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-') f=-1;c=getchar();};while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
int n,m,s,t;
struct node{int to,nxt;ll cap;
}p[3000500];
int fi[N],cnt;
void addline(int x,int y,ll cap){p[++cnt]=(node){y,fi[x],cap};fi[x]=cnt;p[++cnt]=(node){x,fi[y],0};fi[y]=cnt;
}
int col[N],cur[N];
queue<int>q;
int bfs(){memset(col,0,sizeof(col));col[s]=1;q.push(s);while(!q.empty()){int now=q.front();q.pop();for(int i=cur[now]=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(col[to]||!p[i].cap) continue;col[to]=col[now]+1;q.push(to);}}return col[t];
}
ll dfs(int x,ll lim){if(x==t||!lim) return lim;ll res=0;for(int &i=cur[x];~i;i=p[i].nxt){int to=p[i].to;if(!p[i].cap||col[to]!=col[x]+1) continue;ll add=dfs(to,min(lim,p[i].cap));lim-=add;res+=add;p[i].cap-=add;p[i^1].cap+=add;if(!lim) break;}if(lim) col[x]=-1;return res;
}
ll dinic(){ll res=0;while(bfs()){while(ll tmp=dfs(s,2e15)) res+=tmp;}return res;
}
int vis[N];
void find1(int x){if(vis[x]) return;vis[x]=1;for(int i=fi[x];~i;i=p[i].nxt){if(!p[i].cap) continue;find1(p[i].to);}return;
}
void find2(int x){if(vis[x]) return;vis[x]=2;for(int i=fi[x];~i;i=p[i].nxt){if(!p[i^1].cap) continue;find2(p[i].to);}return;
}
#define id(x,y) ((x-1)*m+y)
int num,x,tot;
int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};
inline bool exi(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;
}
int a[55][55],mx[2505];
struct tower{int x,y,op;int mx,xx,yy;
}o[2505];
#define heng(x,y) ((x-1)*m+y)
#define shu(x,y) ((x-1)*m+y+n*m)
int main(){memset(fi,-1,sizeof(fi));cnt=-1;n=read();m=read();for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){x=read();if(x>=0) a[i][j]=x;else{x=-x;o[++num]=(tower){i,j,x,0,i,j};}}}//printf("ok");for(int i=1;i<=num;i++){int x=o[i].x,y=o[i].y,op=o[i].op;while(x>=1&&x<=n&&y>=1&&y<=m){x+=dx[op];y+=dy[op];if(a[x][y]>o[i].mx){o[i].mx=a[x][y];o[i].xx=x;o[i].yy=y;}}tot+=o[i].mx;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++) addline(heng(i,j),shu(i,j),2e15);}s=2*n*m+1;t=s+1;for(int i=1;i<=num;i++){int x=o[i].x,y=o[i].y,op=o[i].op;int xx=o[i].xx,yy=o[i].yy,mx=o[i].mx;//a[x][y]=2e9;if(op<=2){addline(shu(x,y),t,2e15);while(xx!=x||yy!=y){// printf("(%d %d)->(%d %d) len=%d\n",xx,yy,xx-dx[op],yy-dy[op],a[xx-dx[op]][yy-dy[op]]);addline(shu(xx,yy),shu(xx-dx[op],yy-dy[op]),mx-a[xx-dx[op]][yy-dy[op]]);xx-=dx[op];yy-=dy[op];}}else{addline(s,heng(x,y),2e15);while(x!=xx||y!=yy){// printf("(%d %d)->(%d %d) len=%d\n",x,y,x+dx[op],y+dy[op],a[x][y]);addline(heng(x,y),heng(x+dx[op],y+dy[op]),mx-a[x][y]);x+=dx[op];y+=dy[op];}}}printf("%d\n",tot-dinic());return 0;
}
/*
3 2 1 3
1 2 10
2 3 10
*/