POJ.2175.Evacuation Plan(消圈)
\(Description\)
\(n\)个建筑物,每个建筑物里有\(a_i\)个人;\(m\)个避难所,每个避难所可以容纳\(b_i\)个人。
给出每个建筑物及避难所的坐标,任意两点间的距离为它们的曼哈顿距离\(+1\)。
现在给出一个分配方案(\(g[i][j]\)表示第\(i\)个建筑物去第\(j\)个避难所的人数),问是否存在所有人移动的距离之和比当前更小的方案。如果存在,输出任意一组更小的方案。
\(n,m\leq100\)
\(Solution\)
直接跑费用流会T,但是也没必要。
如果残量网络存在负环(只走有流量的边),那么沿该负环继续增广,可以得到一组更优的解。否则就是当前流量下的最小费用流。
所以建出残量网络,找出负环来,在负环上走一遍就行了。
\(S\to i\)的每条边的流量肯定是流满的,所以不需要建(\(i'\to T\)的就不一定了)。
复杂度\(O(nm)\)。(\(m\)是边数)
//688K 329MS
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=205,M=(105*105+N)*2,INF=0x3f3f3f3f;
int Enum,H[N],nxt[M],to[M],cost[M],dis[N],pre[N],g[105][105];
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now*f;
}
inline void AE(int u,int v,int w,int c)
{
if(w) to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, cost[Enum]=c;
}
int SPFA(const int n)
{
static int tm[N];
static bool inq[N];
std::queue<int> q;
memset(dis,0x3f,n+1<<2);
dis[n]=0, q.push(n), tm[n]=1;
while(!q.empty())
{
int x=q.front(); q.pop();
inq[x]=0;
for(int i=H[x],v; i; i=nxt[i])
if(dis[to[i]]>dis[x]+cost[i])
{
dis[v=to[i]]=dis[x]+cost[i], pre[v]=x;
if(!inq[v])
{
if(++tm[v]>=n) return v;
q.push(v), inq[v]=1;
}
}
}
return -1;
}
void FindCircle(int x,const int n)
{
static bool vis[N];
while(!vis[x]) vis[x]=1, x=pre[x];//p不一定在环上(但是能回到环),应该先找出来啊。。
int tmp=x;
do
{
int v=pre[x];//v->x
if(x<=n && v>n) --g[x][v-n];
else if(x>n && v<=n) ++g[v][x-n];
x=v;
}while(x!=tmp);
}
int main()
{
static int a[N],b[N],c[N],d[N],cap[N],use[N];
const int n=read(),m=read(),T=n+m+1;
for(int i=1; i<=n; ++i) a[i]=read(),b[i]=read(),read();
for(int i=1; i<=m; ++i) c[i]=read(),d[i]=read(),cap[i]=read();
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
{
use[j]+=g[i][j]=read();
int cost=std::abs(a[i]-c[j])+std::abs(b[i]-d[j])+1;
AE(i,j+n,INF-g[i][j],cost), AE(j+n,i,g[i][j],-cost);//negative!
}
for(int i=1; i<=m; ++i) AE(i+n,T,cap[i]-use[i],0), AE(T,i+n,use[i],0);
int p=SPFA(T);
if(p==-1) puts("OPTIMAL");
else
{
FindCircle(p,n), puts("SUBOPTIMAL");
for(int i=1; i<=n; ++i,putchar('\n'))
for(int j=1; j<=m; ++j) printf("%d ",g[i][j]);
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------