正题
题面链接:https://gmoj.net/senior/#main/show/5057
题目大意
n∗mn*mn∗m的网格上有一些炮和敌军,每个炮可以攻击在它方向上一个敌军,但是要求炮弹的轨迹不能交叉。求最多打死多少敌军。
解题思路
我们先把炮分成两类,一类是横着打(定义为正类),一类是竖着打(定义为负类),那么肯定是两类不同的轨迹交叉。
我们对于每个炮弹,它每走一格,可能能够多打一些敌方,我们可以把这个多大的定义为这个点的权值。显然对于每一类,格子都会有一个权值。
然后我们大致构建一个这样的图(炮1的第二个与炮2的第二个有交叉)
- 割掉权值为www(这里的www统指那个点在那一类的权值)的边表示不选择这个点
- 正类的边连向炮的方向是因为如果我们要让交叉的那条边不生效,也就是要把后面三个的www权值舍弃,那么就表示炮弹不会打到后面那三个
- 反类的边从炮弹出去也是一个原理,我们要求舍去的一定是一个后缀
然后这就是一个经典的最大权闭合图模板了,上网络流即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=6000,inf=2147483647/3;
const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
struct node{int to,next,w;
}a[N<<8];
int n,m,tot,s,t,cnt,ans;
int dep[N],ls[N];
int v[51][51],wv[51][51];
queue<int> q;
void addl(int x,int y,int w){if(!w)return;a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;return;
}
bool bfs(){memset(dep,0,sizeof(dep));dep[s]=1;while(!q.empty())q.pop();q.push(s);while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[y]||!a[i].w)continue;dep[y]=dep[x]+1;if(y==t)return 1;q.push(y);}}return 0;
}
int dinic(int x,int flow){int rest=0,k;if(x==t)return flow;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[x]+1!=dep[y]||!a[i].w)continue;rest+=(k=dinic(y,min(flow-rest,a[i].w)));a[i].w-=k;a[i^1].w+=k;if(rest==flow)return flow;}if(!rest)dep[x]=0;return rest;
}
int main()
{scanf("%d%d",&n,&m);s=0;t=cnt=tot=1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&wv[i][j]);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int tmp=0,x=i,y=j;if(wv[x][y]<0){int k=-wv[x][y]-1,last=++cnt,tmpp=0;if(k>1)continue;while(1){x+=dx[k];y+=dy[k];if(x<1||y<1||x>n||y>m)break;tmp=max(tmp,wv[x][y]);cnt++;addl(s,cnt,tmp-tmpp);if(last)addl(cnt,last,inf);tmpp=tmp;last=v[x][y]=cnt;}ans+=tmp;}}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int tmp=0,x=i,y=j;if(wv[x][y]<0){int k=-wv[x][y]-1,last=++cnt,tmpp=0;if(k<2)continue;while(1){x+=dx[k];y+=dy[k];if(x<1||y<1||x>n||y>m)break;tmp=max(tmp,wv[x][y]);cnt++;addl(cnt,t,tmp-tmpp);if(last)addl(last,cnt,inf);if(v[x][y])addl(v[x][y],cnt,inf);tmpp=tmp;last=cnt;}ans+=tmp;}}while(bfs())ans-=dinic(s,inf);printf("%d\n",ans);return 0;
}