今天学了最大权闭合子图。。然后找了这道题,发现完全不会。。。。。
看了题解发现这种有诸如选了一个就一定要选另外的一些的限制又要求最优值的题有的可以转化成最大权闭合子图,
这个题我们首先想到不会选相交的区间,
因为那样代价多算了一遍又不会增加价值。然后就是选一些区间,就有了一个经典的限制:
如果选了区间[i,j]就一定要选[i+1,j]和[i,j-1];
关于代价的处理我们可以对每一个[i,i]连到一个权值为-m*a[i]*a[i]的点,然后把每个[i,i]减去a[i],这里关键是把题意理解好就行了;
//图论模型,难在建图; #include<iostream> #include<cstring> #include<cmath> #include<cstdio> #include<algorithm> using namespace std; const int maxn=111,maxa=1000,inf=1e9; int D[maxn*maxn+maxa],t=1,n,m,S,T,sum,last[maxn*maxn+maxa],a[maxn],d[maxn][maxn],num[maxn][maxn]; struct edg{ int nxt,to,f; }e[maxn*maxn*6+maxa*2]; void add(int x,int y,int z){ ++t;e[t].nxt=last[x];last[x]=t;e[t].to=y;e[t].f=z; ++t;e[t].nxt=last[y];last[y]=t;e[t].to=x;e[t].f=0; } int q[maxn*maxn+maxa],head,tail; int bfs(){ head=tail=0;memset(D,-1,sizeof(D)); q[++tail]=S;D[S]=0; while(head<tail){ int u=q[++head]; for(int i=last[u];i;i=e[i].nxt){ int v=e[i].to; if(e[i].f&&D[v]==-1){ D[v]=D[u]+1;q[++tail]=v; } } } return D[T]!=-1; } int dfs(int x,int h){ if(x==T){return h;} int tmp=0,cp; for(int i=last[x];i;i=e[i].nxt){ int v=e[i].to; if(e[i].f&&D[v]==D[x]+1){ cp=dfs(v,min(h-tmp,e[i].f)); e[i].f-=cp;e[i^1].f+=cp;tmp+=cp; } } if(!tmp)D[x]=-2; return tmp; } int dinic(){ int pp,res=0; while(bfs()){ while(pp=dfs(S,inf))res+=pp; } return res; } int main(){ cin>>n>>m; for(int i=1;i<=n;++i)scanf("%d",&a[i]); S=1;T=S+maxa+n*n+1; for(int i=1;i<=maxa;++i)add(S+i,T,m*i*i); int cnt=0; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j){ scanf("%d",&d[i][j]); ++cnt,num[i][j]=S+maxa+cnt; } for(int i=1;i<=n;++i) for(int j=i;j<=n;++j){ if(i==j){ d[i][j]-=a[i]; add(num[i][j],S+a[i],inf); } else{ add(num[i][j],num[i+1][j],inf); add(num[i][j],num[i][j-1],inf); } if(d[i][j]>0){ sum+=d[i][j]; add(S,num[i][j],d[i][j]); } else{ add(num[i][j],T,-d[i][j]); } } printf("%d",sum-dinic()); //system("pause"); return 0; }