这个题出的太好了~ 最开始以为是书上说的最小费用流做,但却果断的TLE了。后来网上参考别人的做法,才知道是消圈做,也就是说最后输出的答案不一定是最小费用的,只要比当前费用低就行了,所以根据最小费用流的证明,若当前的剩余图存在负圈,那么当前的费用一定不是最小的。则我们可以找到这个圈,然后对该圈的每个值进行流量为1的更新操作即可。需要注意的是spfa找圈的时候返回的不一定是圈内的点,所以先要找到圈内的点,然后在循环枚举所有圈内点的过程中也要注意不能有圈外的点出现(在这里WA了很久)
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int inf=1<<28;
const int maxn=100000;
int n,m,x[maxn],y[maxn],b[maxn],p[maxn],q[maxn],c[maxn],s[110][110];
int e,st,des,head[maxn],pnt[maxn],cost[maxn],flow[maxn],nxt[maxn];
int dist[maxn],pre[maxn],ffto[maxn],cnt[maxn];
bool vis[maxn];
queue<int> pq;
void AddEdge(int u,int v,int c,int f1,int f2)
{
pnt[e]=v;nxt[e]=head[u];cost[e]=c;flow[e]=f1;head[u]=e++;
pnt[e]=u;nxt[e]=head[v];cost[e]=-c;flow[e]=f2;head[v]=e++;
}
int Dis(int i,int j)
{
return abs(x[i]-p[j])+abs(y[i]-q[j])+1;
}
int Spfa()
{
while(!pq.empty())
pq.pop();
for(int i=0;i<=des;i++)
dist[i]=inf;
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
memset(pre,-1,sizeof(pre));
dist[st]=0;
vis[st]=1;
cnt[st]++;
pq.push(st);
while(!pq.empty())
{
int u=pq.front();
vis[u]=0;
pq.pop();
for(int i=head[u];i!=-1;i=nxt[i])
if(flow[i]&&dist[pnt[i]]>dist[u]+cost[i])
{
dist[pnt[i]]=dist[u]+cost[i];
pre[pnt[i]]=u;
if(!vis[pnt[i]])
{
pq.push(pnt[i]);
vis[pnt[i]]=1;
if(++cnt[pnt[i]]>n+m+2)
return pnt[i];
}
}
}
return -1;
}
void solve()
{
int ss=Spfa();
int sta=ss;
if(ss==-1)
{
printf("OPTIMAL\n");
return;
}
printf("SUBOPTIMAL\n");
memset(vis,0,sizeof(vis));
for(;!vis[ss];vis[ss]=1,ss=pre[ss]);
sta=ss;
do
{
int from=pre[sta],to=sta;
if(from<=n&&to>n)
s[from][to-n]++;
if(to<=n&&from>n)
s[to][from-n]--;
sta=pre[sta];
}while(sta!=ss);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
printf("%d%c",s[i][j],j==m?'\n':' ');
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,-1,sizeof(head));
memset(ffto,0,sizeof(ffto));
e=st=0,des=n+m+1;
for(int i=1;i<=n;i++)
scanf("%d%d%d",&x[i],&y[i],&b[i]);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&p[i],&q[i],&c[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&s[i][j]);
ffto[j]+=s[i][j];
AddEdge(i,n+j,Dis(i,j),inf-s[i][j],s[i][j]);
}
for(int i=1;i<=n;i++)
AddEdge(st,i,0,b[i],0);
for(int i=1;i<=m;i++)
AddEdge(n+i,des,0,c[i]-ffto[i],ffto[i]);
solve();
}
return 0;
}