大意
选择某种区间[i,j]可以获得收益(有正有负),选某一种寿司需要付出一定的代价,求收益-代价的最大值
思路
用最小割解决;
将每一个区间作为一个点
将与S在一个集合的作为不选的点,将与T在一个集合的最为选择的点。
与S的连边表示选择的代价,与T的连边作为不选放弃的收益
只需要解决三个问题
1. 边权有正有负
2. 要求对于某种标号有一个m*x*x的代价
3. 符合题意中的区间包含关系
问题1: 与如果选择d[i,j]有正收益的话可以向T连边(不选要放弃这些收益),如果为负的话可以看做是一种代价改为向S连正权变。
问题2: 可以从S向标号pos连(m*x*x)的边,再由pos向归属于pos的寿司连inf边,S向寿司连标号代价的边, 这样选择任何一个寿司就一定要删掉S与pos的边,而选择寿司要花费标号的代价。
问题3: 对于
[i,j]
[
i
,
j
]
这个区间可以从
[i+1,j]
[
i
+
1
,
j
]
[i,j−1]
[
i
,
j
−
1
]
向它连边就一定可以将所有要加的区间全部限制住
即选择
[i,j]
[
i
,
j
]
就一定要选择这些区间 这么连边和从
[i,j]
[
i
,
j
]
向每一个区间连边是等效的。
代码
精简洗练 又臭又长
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=5000+5;
const int N=5000000+5;
const int inf=0x7ffffff;
struct node
{
int v,next,w;
}tb[N*2];
int len=1,h[N];
void add1(int i,int j,int w)
{
len++; tb[len]=(node){j,h[i],w}; h[i]=len;
len++; tb[len]=(node){i,h[j],0}; h[j]=len;
}
int n,m;
int pos[maxn],d[maxn][maxn];
int hash[N][2],cur[N];
int geth(int x,int y)
{
return x*n+y;
}
int tot,s,t,ans;
int dis[N/10],q[N],c[N];
bool bfs();
int dfs(int now,int flow);
void dinic();
void solve();
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d %d",&n,&m);
s=20000+1; t=s+1;
for(int i=1; i<=n; i++)
scanf("%d",&pos[i]);
for(int i=1; i<=n; i++)
{
for(int j=i; j<=n; j++)
{
scanf("%d",&d[i][j]);
int tmp=geth(i,j);
if(d[i][j]<0)
{
if(i!=j)
{
hash[tmp][0]=++tot;
add1(s,hash[tmp][0],-d[i][j]);
}
else hash[tmp][0]=++tot;
}
else
{
ans+=d[i][j];
hash[tmp][0]=++tot;
if(i!=j)
{
add1(hash[tmp][0],t,d[i][j]);
}
}
}
}
solve();
return 0;
}
void solve()
{
for(int i=1; i<=n; i++)
{
int tmp=geth(i,i);
if(!c[pos[i]])
{
c[pos[i]]=++tot;
add1(s,c[pos[i]],m*pos[i]*pos[i]);
}
add1(c[pos[i]],hash[tmp][0],inf);
if(d[i][i]>0)
{
add1(s,hash[tmp][0],pos[i]);
add1(hash[tmp][0],t,d[i][i]);
}
else
{
add1(s,hash[tmp][0],pos[i]-d[i][i]);
}
}
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
{
if(i!=j)
{
int tmp1=geth(i,j-1),tmp2=geth(i,j),tmp3=geth(i+1,j);
add1(hash[tmp1][0],hash[tmp2][0],inf);add1(hash[tmp3][0],hash[tmp2][0],inf);
}
}
}
dinic();
cout<<ans<<endl;
}
bool bfs()
{
memset(dis,-1,sizeof(dis));
int head=0,tail=1;
q[1]=s; dis[s]=0;
while(head<tail)
{
head++; int now=q[head];
if(now==t) return true;
for(int i=h[now]; i ;i=tb[i].next)
{
int v=tb[i].v;
if(dis[v]==-1 && tb[i].w>0)
{
dis[v]=dis[now]+1; tail++; q[tail]=v;
}
}
}
return false;
}
int dfs(int now,int flow)
{
if(now==t || flow==0) return flow;
int tmp=flow;
for(int &i=cur[now]; i ; i=tb[i].next)
{
int v=tb[i].v;
if(dis[v]==dis[now]+1 && tb[i].w)
{
int delta=dfs(v,min(tmp,tb[i].w));
tmp-=delta; tb[i].w-=delta; tb[i^1].w+=delta;
if(tmp==0) break;
}
}
return flow-tmp;
}
void dinic()
{
while(bfs())
{
for(int i=0; i<=t; i++)
{
cur[i]=h[i];
}
ans-=dfs(s,inf);
}
}