本文主要是介绍【NOI2018模拟3.26】Cti,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Description
有一个 n × m 的地图, 地图上的每一个位置可以是空地, 炮塔或是敌人. 你需要操纵炮塔消灭敌人.
对于每个炮塔都有一个它可以瞄准的方向, 你需要在它的瞄准方向上确定一个它的攻击位置,当然也可以不进行攻击. 一旦一个位置被攻击, 则在这个位置上的所有敌人都会被消灭.
保证对于任意一个炮塔, 它所有可能的攻击位置上不存在另外一个炮塔.
定义炮弹的运行轨迹为炮弹的起点和终点覆盖的区域. 你需要求出一种方案, 使得没有两条炮弹轨迹相交.
Input
第一行两个整数 n,m.
接下来 n 行, 每行 m 个整数, 0 表示空地, −1,−2,−3,−4 分别表示瞄准上下左右的炮塔, 正整
数 p 表示表示此位置有 p 个敌人.
Output
一行一个整数表示答案.
Sample Input
输入1:
3 2
0 9
-4 3
0 -1
输入2:
4 5
0 0 -2 0 0
-4 0 5 4 0
0 -4 3 0 6
9 0 0 -1 0
Sample Output
输出1:
9
输出2:
12
Data Constraint
对于前 20% 的数据, n,m ≤ 5;
对于另 20% 的数据, 朝向上下的炮塔至多有 2 个;
对于另 20% 的数据, 至多有 6 个炮塔;
对于 100% 的数据, 1 ≤ n,m ≤ 50, 每个位置的敌人数量 < 1000.
Solution
一眼网络流。
然而这个的建模方法一前没弄过。
最小割模型。
对于横向射出的,依次连边连成一条链。
对于纵向的,依次反向连边练成一条链。
源点向横向的炮塔连正无穷,纵向炮塔向汇点连正无穷。
这样流都会从源点出发沿着边流到汇点。
而因为不能交叉,即不能存在这样的流,即割掉。
一开始把每个炮塔射程上的最大值统计进答案里,割表示选一个小一点的,即减掉一些,就是直接最小割就好了。
但是流从横的流到竖的后不能再流到横的那里。
所以分点就行了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100010
#define INF 214748347
using namespace std;
int S,T,n,m,last[N*10],nxt[N*10],to[N*10],data[N*10],tot=1,ans=0,d[N],bz[N],a[60][60];
void putin(int x,int y,int z)
{
nxt[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;
nxt[++tot]=last[y];last[y]=tot;to[tot]=x;data[tot]=0;
}
bool bfs()
{
int i=0,j=1;memset(bz,0,sizeof(bz));d[1]=S;bz[S]=1;
while(i<j)
{
int x=d[++i];
for(int k=last[x];k;k=nxt[k])
if(bz[to[k]]==0&&data[k]>0) bz[to[k]]=bz[x]+1,d[++j]=to[k];
}
return bz[T]>0;
}
int dinic(int x,int v)
{
if(x==T) return v;
int ans=0;
for(int i=last[x];i;i=nxt[i])
{
int y=to[i],qq=0;
if(bz[y]==bz[x]+1&&data[i]>0)
{
qq=dinic(y,min(v,data[i]));
if(qq) data[i]-=qq,ans+=qq,v-=qq,data[i^1]+=qq;
if(v==0) return ans;
}
}
if(ans==0) bz[x]=-1;
return ans;
}
int p(int x,int y,int z)
{
return (x-1)*m+y+n*m*z;
}
int main()
{
freopen("cti.in","r",stdin);
freopen("cti.out","w",stdout);
scanf("%d%d",&n,&m);
S=2*n*m+1;T=S+1;
fo(i,1,n) fo(j,1,m) scanf("%d",&a[i][j]);
fo(i,1,n)
{
fo(j,1,m)
{
putin(p(i,j,0),p(i,j,1),INF);
if(a[i][j]==-1)
{
int jy=0;
fd(k,i-1,1) jy=max(jy,a[k][j]);
ans+=jy;
a[i][j]=0;
fd(k,i-1,1)
{
putin(p(k,j,1),p(k+1,j,1),jy-a[k+1][j]);
}
putin(p(i,j,1),T,INF);
}
if(a[i][j]==-2)
{
int jy=0;
fo(k,i+1,n) jy=max(jy,a[k][j]);
ans+=jy;
a[i][j]=0;
fo(k,i+1,n)
{
putin(p(k,j,1),p(k-1,j,1),jy-a[k-1][j]);
}
putin(p(i,j,1),T,INF);
}
if(a[i][j]==-3)
{
int jy=0;
fd(k,j-1,1) jy=max(jy,a[i][k]);
ans+=jy;
a[i][j]=0;
fd(k,j-1,1)
{
putin(p(i,k+1,0),p(i,k,0),jy-a[i][k+1]);
}
putin(S,p(i,j,0),INF);
}
if(a[i][j]==-4)
{
int jy=0;
fo(k,j+1,m) jy=max(jy,a[i][k]);
ans+=jy;
a[i][j]=0;
fo(k,j+1,m)
{
putin(p(i,k-1,0),p(i,k,0),jy-a[i][k-1]);
}
putin(S,p(i,j,0),INF);
}
}
}
while(bfs()) ans-=dinic(S,INF);
printf("%d\n",ans);
}
这篇关于【NOI2018模拟3.26】Cti的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!