题目链接:点击查看
题目大意:给出n个建筑物和m个避难所,每个建筑物中的人需要到避难所中去避难,规定花费是建筑物和避难所的曼哈顿距离+1,现在给出一种路线方案,问这个方案是不是最优的,如果不是,输出比当前方案更优的答案
题目分析:虽然看完题目之后感觉是一道最小费用最大流的题目,但是如果直接照做的话会超时,可能是构图卡SPFA了,其实我们可以换个角度去思考,因为题目并不是要求我们输出最优解,而是更优一点的就行,那么我们可以模拟一下整个过程,这个过程叫消圈定理,具体解释详见:点我点我
其实就是根据当前方案的流量建立一张费用图,试着跑一下spfa判断当前图中是否存在负环,如果存在负环的话说明替换该负环上的边后可以达到更优的解,具体建图方法因为不是真的要求费用流,所以并不需要源点提供流量,但是需要汇点中转流量,建好图后以汇点为起点跑spfa就好了,算是一个模板题目了,等跑出负环后再基于负环操作:
- 若当前u->v是建筑物到避难所,则方案+1
- 若当前u->v是避难所到建筑物,则方案-1
在这里需要注意一下,spfa返回的点并不一定是在负环上的点,需要我们操作一波将其转移到负环上的点才能继续操作
代码:
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=210;
int n,m,cnt,head[N],sum[N],ans[N][N];
struct Edge
{
int to,next,w;
}edge[N*N];
void addedge(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
struct Pos
{
int x,y,num;
void input()
{
scanf("%d%d%d",&x,&y,&num);
}
}a[N];
int dis(int x,int y)
{
return abs(a[x].x-a[y+n].x)+abs(a[x].y-a[y+n].y)+1;
}
bool vis[N];
int d[N],num[N],pre[N];
int spfa(int st)
{
int n=st;
memset(pre,-1,sizeof(pre));
memset(vis,false,sizeof(vis));
memset(d,inf,sizeof(d));
memset(num,0,sizeof(num));
queue<int>q;
d[st]=0;
vis[st]=true;
num[st]++;
q.push(st);
while(q.size())
{
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
int w=edge[i].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
pre[v]=u;
if(!vis[v])
{
q.push(v);
num[v]++;
vis[v]=true;
if(num[v]>n)
return v;
}
}
}
}
return -1;
}
void init()
{
memset(sum,0,sizeof(sum));
memset(head,-1,sizeof(head));
cnt=0;
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
while(scanf("%d%d",&n,&m)!=EOF)
{
int S=n+m+1;
init();
for(int i=1;i<=n+m;i++)
a[i].input();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&ans[i][j]);
addedge(i,j+n,dis(i,j));
if(ans[i][j])
addedge(j+n,i,-dis(i,j));
sum[j]+=ans[i][j];
}
}
for(int i=1;i<=m;i++)
{
if(sum[i]>0)
addedge(S,i+n,0);
if(sum[i]<a[i+n].num)
addedge(i+n,S,0);
}
int id=spfa(S);
if(id==-1)
puts("OPTIMAL");
else
{
puts("SUBOPTIMAL");
memset(vis,false,sizeof(vis));
int v=id;
while(!vis[v])
{
vis[v]=true;
v=pre[v];
}
id=v;
do
{
int u=pre[v];//u->v
if(u<=n&&v>n&&v!=S)
ans[u][v-n]++;
if(v<=n&&u>n&&u!=S)
ans[v][u-n]--;
v=u;
}while(v!=id);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
printf("%d ",ans[i][j]);
printf("\n");
}
}
}
return 0;
}