COGS1873-happiness
[FJOI 2017 四校联训 Round 11]小猪送货
第一题
显而易见,这道题的每个位置决策都会对周围位置的决策产生影响,且需要满足“收益最优”。点数比较多,想到最小割建模。
今天第一次仔细阅读了《浅析一类最小割问题-彭天翼》,为自己总结一下:
1.应用范围:存在多个二元关系,每个二元关系满足“若两个不相同,则增加v的花费”。必要的话,可以通过将权值取反来保证最小割的性质。
2.建模方法:
1)用流量表示花费(这里边权为容量)。
2)赋予当前有向边含义:割S集表示选择甲,割T集表示选乙;或割a表示A选甲,割c表示A选乙,或割 表示B选乙,割d表示B选甲。
3)方便起见,把A、B之间的两条有向边强制权值相同。
3.列方程求解容量。
{
a+b=v1
c+d=v2
a+e+d=v3
b+e+c=v4
}
构造容易表示的一组解。
4.边权权为负的情况。
1)若e为负。改变有向边含义。
2)若a,b,c,d为负。由于a,c和b,d有且只能割一条,所以将容量都扩大到正数,最后在答案上修改。
5.边权变化对答案的影响。
1)不说也罢,边权的改变一定要按照顺序把答案变回来。可以把边权扩大到正,可以为了不丢浮点精度把边权乘上一个数……总之是有细节的。
题解:
按照所述一般情况构图。注意选文理科的时候,有当前位置本身的收益和附加的收益。若按照如上方法直接构图,会出现类似“并联边”的情况。理论上对答案没有影响,但增加了不少常数。所以与源汇点连边时,将总容量下来,最后同意对源汇点连边。
最后,一个点算上连向汇点的边共有5条出边,这是用当前弧优化可以很好的砍常数。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define R register
#define dmin(_a,_b)(_a)<(_b)?(_a):(_b)
#define dmax(_a,_b)(_a)>(_b)?(_a):(_b)
#define get(_x,_y)((_x-1)*M+_y)
#define inf 1<<29
using namespace std;
struct Edge{
int to,nex,f;}edge[145000];
int N,M,S,T,delta,et,st[11100],cur[11100];
int level[11100],q[11100],l,r;
int ns[11100],nt[11100],v1[11100],v2[11100],v3[11100],v4[11100];
void read(int &aa)
{
R char ch;while(ch=getchar(),ch<'0'||ch>'9');aa=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')aa=aa*10+ch-'0';
}
void add(int a,int b,int c)
{
edge[et]=(Edge){b,st[a],c},st[a]=et++;
edge[et]=(Edge){a,st[b],0},st[b]=et++;
}
bool bfs()
{
memset(level,0,sizeof(level));
l=0,r=1,q[0]=S,level[S]=1;
R int u,v,e;
while(l<r)
{
u=q[l++];
for(e=st[u];e!=-1;e=edge[e].nex)
if(!level[edge[e].to]&&edge[e].f>0)
{
v=edge[e].to;
level[v]=level[u]+1;
q[r++]=v;
if(v==T)return true;
}
}
return false;
}
int dfs(int u,const int flow)
{
if(u==T)return flow;
R int v,f,ret=0,tmp;
for(int &e=cur[u];e!=-1;e=edge[e].nex)
if(level[edge[e].to]==level[u]+1&&edge[e].f>0)
{
v=edge[e].to;
tmp=dmin(flow-ret,edge[e].f);
f=dfs(v,tmp);
edge[e].f-=f,edge[e^1].f+=f,ret+=f;