第一次遇到加了“多余”的边会导致WA的——在我看来是很多余,见代码191行
之后会思考为什么,想出来再更。
//http://www.renfei.org/blog/isap.html 带解释的
//https://www.cnblogs.com/bosswnx/p/10353301.html 形式和我的比较相近的
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define maxe 100024 //pay 双向边 一共10万条路 双向就是20万 反边就是40万
#define maxv 1024 //pay
#define maxn 25 //pay
#define sc scanf
#define pt printf
#define rep(i,a,b) for(int i=(a);i<(b);++i)
const int inf = 0x3f3f3f3f;
int cg,sp,ins; //cg change sp是总流量 ins是加速回溯点
int N,M,T,D ,s,t,delta;
int q[maxv],fro,rea;
typedef struct ed{
int v,nxt,cap; //dis
}ed;
ed e[maxe];
int tot,head[maxv],cur[maxv],vis[maxv],bk[maxv],d[maxv],num[maxv]; //
int mi(int a,int b) {return a<b?a:b;}
int mx(int a,int b) {return a>b?a:b;}
void add(int u,int v,int cap)
{
e[tot].v=v; e[tot].nxt=head[u];
/*e[tot].dis=dis;*/ e[tot].cap=cap;
head[u]=tot++;
e[tot].v=u; e[tot].nxt=head[v];
/*e[tot].dis=-dis;*/ e[tot].cap=0;
head[v]=tot++;
}
// 仅有一次的BFS为ISAP节省了不少时间
bool bfs()
{
//数组模拟queue
int u,v,i;
for(i=0;i<=t;++i) vis[i]=num[i]=0;
// memset(vis, 0, sizeof(vis));
// memset(num, 0, sizeof(num));
fro = rea = 0;
q[rea] = t; ++rea;
vis[t] = 1;
d[t] = 0;
++num[d[t]];
while (rea>fro)
{
u = q[fro]; ++fro;
for (i=head[u]; i!=-1; i=e[i].nxt)
{
v=e[i].v;
if (!vis[v] && e[i^1].cap )
{
vis[v] = true;
d[v] = d[u] + 1;
++num[d[v]];
q[rea] = v; ++rea;
}
}
}
return vis[s];
}
// 增广
int augment()
{
int flow = inf, i;
cg = t;
// 从汇点到源点通过 p 追踪增广路径, flow 为一路上最小的残量
while (cg != s) {
i = bk[cg];
if(flow>=e[i].cap)
{
flow = e[i].cap;
ins = e[i^1].v;
//用来加速寻找,在最小流量断开的地方重新开始寻找
//嗯,等一下 我这个是从终点往起点寻找,而确定增光路径是从起点到终点
//那么起点是河流的上游,那么回溯的河段应该尽可能的往上游靠近
//所以应该将flow>e[i].cap的大于号改成大于等于号
}
cg = e[i^1].v;
}
cg = t;
// 从汇点到源点更新流量
while (cg != s) {
i = bk[cg];
e[i].cap -= flow;
e[i^1].cap += flow;
cg = e[i^1].v;
}
return flow;
}
//由于每次修改层次的时候,都是在到剩下子节点的距离中挑选最短的加1 所以层次分明不会出现死循环
int max_flow()
{
int flow = 0,i,u,v;
bool advanced;
if(bfs()==false) return 0;
//不一定是从s到t,你要知道统计每个层次的点的个数是全局统计的
u = s;
memcpy(cur, head, sizeof(head));
while (d[s] < t+1) //层次问题!!! 一定要保证t是最大的
//终点是0,那么起点所在层次最多是N-1 同理,不是d[s]<t
{
if (u == t)
{
flow += augment();
u = ins; //pay speed up
}
advanced = false;
for (i = cur[u]; i!=-1; i=e[i].nxt)
{
v = e[i].v;
if (e[i].cap && d[u] == d[v] + 1)
{
advanced = true;
bk[v] = i;
cur[u] = i;
u = v;
break;
}
}
if (!advanced)
{ // retreat
int m = t+1; //层次问题!!! 一定要保证t是最大的
for (i = head[u]; i != -1; i=e[i].nxt)
{
if (e[i].cap&&m>d[e[i].v])
{
cur[u] = i;
m = d[e[i].v];
}
}
if (--num[d[u]] == 0) break; // gap 优化
++num[d[u] = m+1];
//我以前一直在想 如果没有找到怎么办呢 现在发现原来找不到的话距离会被赋成N+1
if (u != s)
u = e[bk[u]^1].v;
}
}
return flow;
}
void init()
{
tot=0;
memset(head,-1,sizeof(head)); //pay
}
char times[maxn][maxn],haves[maxn][maxn];
//最大流部分没有什么问题了 关键在于终点源点、编号分配、题目理解建图上面
int main()
{
freopen("in.txt","r",stdin);
sc("%d",&T);
s=0, bk[0]=-1;
int i,j,u,v,id,can,kase=0,who,LI;
while(T--)
{
++kase;
sc("%d%d",&N,&D);
init(); sp = LI = 0;
for(i=0;i<N;++i) sc("%s",times[i]);
for(i=0;i<N;++i) sc("%s",haves[i]);
M=strlen(times[0]); delta = (N)*(M); t=2*delta+1; s=2*delta;
for(i=0;i<N;++i) for(j=0;j<M;++j)
{
can = times[i][j]-'0';
if(can==0) continue;
id = i*(M) + (j);
add(id,id + delta,can );
}
for(i=0;i<N;++i) for(j=0;j<M;++j)
{
if(haves[i][j]!='L') continue;
id = i*(M) + (j);
add(s,id,1);
++LI;
}
for(i=0;i<N;++i) for(j=0;j<M;++j)
{
if(times[i][j]=='0') continue; //这里写成过有没有蜥蜴的那个矩阵
id = i*(M) + (j);
if(i-D<0||i+D>N-1||j-D<0||j+D>M-1)
{
add(id+delta,t,inf);
}
for(u=mx(0,i-D);u<=mi(N-1,i+D);++u) for(v=mx(0,j-D);v<=mi(M-1,j+D);++v)
{
//下面这一行居然是决胜关键 可是废点不是完全不会走吗
if(times[u][v]=='0') continue;
if(u==i&&v==j) continue;
//这是最终确定的可跳跃方式,他这个居然是曼哈顿距离 待会交一下看一下是不是这样 wa 了
if(abs(u-i)+abs(v-j)>D) continue;
//pt("i=%d,j=%d,u=%d,v=%d\n",i,j,u,v);
who = u*(M) + (v);
add(id+delta,who,inf);
}
}
sp = max_flow();
int ans = LI-sp;
if(ans==0) printf("Case #%d: no lizard was left behind.\n",kase);
else if(ans==1) printf("Case #%d: 1 lizard was left behind.\n",kase);
else printf("Case #%d: %d lizards were left behind.\n",kase,ans);
}
return 0;
}
//http://www.renfei.org/blog/isap.html 带解释的//https://www.cnblogs.com/bosswnx/p/10353301.html 形式和我的比较相近的 #include<cstdio>#include<cstring>#include<cmath>using namespace std;#define maxe 100024 //pay 双向边 一共10万条路 双向就是20万 反边就是40万#define maxv 1024 //pay#define maxn 25 //pay#define sc scanf#define pt printf#define rep(i,a,b) for(int i=(a);i<(b);++i)const int inf = 0x3f3f3f3f; int cg,sp,ins; //cg change sp是总流量 ins是加速回溯点int N,M,T,D ,s,t,delta;int q[maxv],fro,rea;typedef struct ed{ int v,nxt,cap; //dis}ed;ed e[maxe];int tot,head[maxv],cur[maxv],vis[maxv],bk[maxv],d[maxv],num[maxv]; //int mi(int a,int b) {return a<b?a:b;}int mx(int a,int b) {return a>b?a:b;}void add(int u,int v,int cap){ e[tot].v=v; e[tot].nxt=head[u]; /*e[tot].dis=dis;*/ e[tot].cap=cap; head[u]=tot++;
e[tot].v=u; e[tot].nxt=head[v]; /*e[tot].dis=-dis;*/ e[tot].cap=0; head[v]=tot++;} // 仅有一次的BFS为ISAP节省了不少时间 bool bfs(){ //数组模拟queue int u,v,i; for(i=0;i<=t;++i) vis[i]=num[i]=0; // memset(vis, 0, sizeof(vis)); // memset(num, 0, sizeof(num)); fro = rea = 0; q[rea] = t; ++rea; vis[t] = 1; d[t] = 0; ++num[d[t]]; while (rea>fro) { u = q[fro]; ++fro; for (i=head[u]; i!=-1; i=e[i].nxt) { v=e[i].v; if (!vis[v] && e[i^1].cap ) { vis[v] = true; d[v] = d[u] + 1; ++num[d[v]]; q[rea] = v; ++rea; } } } return vis[s];}// 增广int augment(){ int flow = inf, i; cg = t; // 从汇点到源点通过 p 追踪增广路径, flow 为一路上最小的残量 while (cg != s) { i = bk[cg]; if(flow>=e[i].cap) { flow = e[i].cap; ins = e[i^1].v; //用来加速寻找,在最小流量断开的地方重新开始寻找 //嗯,等一下 我这个是从终点往起点寻找,而确定增光路径是从起点到终点 //那么起点是河流的上游,那么回溯的河段应该尽可能的往上游靠近 //所以应该将flow>e[i].cap的大于号改成大于等于号 } cg = e[i^1].v; } cg = t; // 从汇点到源点更新流量 while (cg != s) { i = bk[cg]; e[i].cap -= flow; e[i^1].cap += flow; cg = e[i^1].v; } return flow;}//由于每次修改层次的时候,都是在到剩下子节点的距离中挑选最短的加1 所以层次分明不会出现死循环 int max_flow(){ int flow = 0,i,u,v; bool advanced; if(bfs()==false) return 0; //不一定是从s到t,你要知道统计每个层次的点的个数是全局统计的 u = s; memcpy(cur, head, sizeof(head)); while (d[s] < t+1) //层次问题!!! 一定要保证t是最大的 //终点是0,那么起点所在层次最多是N-1 同理,不是d[s]<t { if (u == t) { flow += augment(); u = ins; //pay speed up } advanced = false; for (i = cur[u]; i!=-1; i=e[i].nxt) { v = e[i].v; if (e[i].cap && d[u] == d[v] + 1) { advanced = true; bk[v] = i; cur[u] = i; u = v; break; } } if (!advanced) { // retreat int m = t+1; //层次问题!!! 一定要保证t是最大的 for (i = head[u]; i != -1; i=e[i].nxt) { if (e[i].cap&&m>d[e[i].v]) { cur[u] = i; m = d[e[i].v]; } } if (--num[d[u]] == 0) break; // gap 优化 ++num[d[u] = m+1]; //我以前一直在想 如果没有找到怎么办呢 现在发现原来找不到的话距离会被赋成N+1 if (u != s) u = e[bk[u]^1].v; } } return flow;}
void init(){ tot=0; memset(head,-1,sizeof(head)); //pay }char times[maxn][maxn],haves[maxn][maxn];//最大流部分没有什么问题了 关键在于终点源点、编号分配、题目理解建图上面int main(){ freopen("in.txt","r",stdin); sc("%d",&T); s=0, bk[0]=-1; int i,j,u,v,id,can,kase=0,who,LI; while(T--) { ++kase; sc("%d%d",&N,&D); init(); sp = LI = 0; for(i=0;i<N;++i) sc("%s",times[i]); for(i=0;i<N;++i) sc("%s",haves[i]); M=strlen(times[0]); delta = (N)*(M); t=2*delta+1; s=2*delta; for(i=0;i<N;++i) for(j=0;j<M;++j) { can = times[i][j]-'0'; if(can==0) continue; id = i*(M) + (j); add(id,id + delta,can ); } for(i=0;i<N;++i) for(j=0;j<M;++j) { if(haves[i][j]!='L') continue; id = i*(M) + (j); add(s,id,1); ++LI; } for(i=0;i<N;++i) for(j=0;j<M;++j) { if(times[i][j]=='0') continue; //这里写成过有没有蜥蜴的那个矩阵 id = i*(M) + (j); if(i-D<0||i+D>N-1||j-D<0||j+D>M-1) { add(id+delta,t,inf); } for(u=mx(0,i-D);u<=mi(N-1,i+D);++u) for(v=mx(0,j-D);v<=mi(M-1,j+D);++v) { //下面这一行居然是决胜关键 可是废点不是完全不会走吗 if(times[u][v]=='0') continue; if(u==i&&v==j) continue; //这是最终确定的可跳跃方式,他这个居然是曼哈顿距离 待会交一下看一下是不是这样 wa 了 if(abs(u-i)+abs(v-j)>D) continue; //pt("i=%d,j=%d,u=%d,v=%d\n",i,j,u,v); who = u*(M) + (v); add(id+delta,who,inf); } } sp = max_flow(); int ans = LI-sp; if(ans==0) printf("Case #%d: no lizard was left behind.\n",kase); else if(ans==1) printf("Case #%d: 1 lizard was left behind.\n",kase); else printf("Case #%d: %d lizards were left behind.\n",kase,ans); } return 0;}