传送门
火星探险
题意:给定网格,其中放有只能被拿取一次的岩石,求从左上角格点到右下角格点的k条路径,使得拿取的岩石总数最多。
I think
最大费用流,将点拆至x,y两个集合.对于格点i在x集合中的对应点,向i在y集合中的对应点连容量为Inf,费用为0的边,若其置放岩石,再向y集中的对应点了连容量为1费用为1的边,向其右边和下边的在x集中的对应点连容量为Inf,费用为0的边.
乍一看这道题和深海机器人很像,于是用同样的做法第一遍WA.为什么?深海机器人题给的本来就是边权,但本题相当于给出的是点权,点权只能够取一次,于是需要通过拆点来限流.
Code
#include<cstdio>
#include<queue>
using namespace std;
const int sm = 2460;
const int sn = 9900;
const int Inf = 0x3f3f3f3f;
int K,Q,P,S,T,tot=1,tmp;
int mk[36][36],mp[36][36];
int pre[sm],vis[sm],cst[sm];
int to[sn],nxt[sn],hd[sm],c[sn],_c[sn],f[sn];
int Min(int x,int y) { return x<y?x:y; }
bool chk(int x,int y) { return (x<=Q&&x>=1&&y<=P&&y>=1&&mk[x][y]!=1); }
void Add(int u,int v,int x,int y) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,_c[tot]=c[tot]=x,f[tot]=y;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,_c[tot]=c[tot]=0,f[tot]=-y;
}
void SPFA() {
int df,t;
queue<int>Que;
while(true) {
for(int i=1;i<=T;++i) pre[i]=0,cst[i]=-Inf;
Que.push(S),cst[S]=0;
while(!Que.empty()) {
t=Que.front(),Que.pop();
vis[t]=0;
for(int i=hd[t];i;i=nxt[i])
if(c[i]>0&&cst[to[i]]<cst[t]+f[i]) {
cst[to[i]]=cst[t]+f[i];
pre[to[i]]=i;
if(!vis[to[i]]) {
vis[to[i]]=1;
Que.push(to[i]);
}
}
}
if(cst[T]==-Inf) break;
df=Inf;
for(int i=T;i!=S;i=to[pre[i]^1])
df=Min(df,c[pre[i]]);
for(int i=T;i!=S;i=to[pre[i]^1])
c[pre[i]]-=df,c[pre[i]^1]+=df;
}
}
void Print(int num,int x) {
if(x==mp[Q][P]) return;
for(int i=hd[x];i;i=nxt[i])
if(_c[i]!=c[i]&&i%2==0) {
if(to[i]!=x+tmp) {
if(to[i]==x-tmp+1) printf("%d %d\n",num,1);
else printf("%d %d\n",num,0);
}
c[i]++,Print(num,to[i]);
return;
}
}
int main() {
int cnt=0;
scanf("%d%d%d",&K,&P,&Q);
for(int i=1;i<=Q;++i)
for(int j=1;j<=P;++j) {
scanf("%d",&mk[i][j]);
mp[i][j]=++cnt;
}
tmp=Q*P,S=(Q*P<<1)+1,T=S+1;
Add(S,mp[1][1],K,0);
Add(mp[Q][P]+tmp,T,K,0);
for(int i=1;i<=Q;++i)
for(int j=1;j<=P;++j) {
if(mk[i][j]==1) continue;
if(mk[i][j]==2) Add(mp[i][j],mp[i][j]+tmp,1,1);
Add(mp[i][j],mp[i][j]+tmp,Inf,0);
if(chk(i+1,j)) Add(mp[i][j]+tmp,mp[i+1][j],Inf,0);
if(chk(i,j+1)) Add(mp[i][j]+tmp,mp[i][j+1],Inf,0);
}
SPFA();
for(int i=1;i<=K;++i)
Print(i,mp[1][1]);
return 0;
}