对于 100%的数据,n≤200,k≤10,0≤a[i]<=10^6以上数据有交集
题解:这道题我写的最大费用最大流。刚开始是按照k==1的情况思考的,想拿些部分分,结果A了。
先说怎么见图吧。
首先拆点,拆成的两个点之间连一条容量为1,价值为a[i]的边。然后从源点向每个点连一条容量为1,价值为0的边,
从每个点向汇点连一条容量为1,价值为0的边。因为相邻的n各点只能取k个,那么我们强制所选的每一条路径上的点都是属于不同的快,即两点之间的距离至少为n,就是从i点向i+n和之后的点连边,容量为1,价值为0。最后,把汇点拆开,连边容量为K,价值为0. 这样我们就会最多选择K条路径,而路径上的点都分属不同的快,这样的话每个快最多选取K个点,即保证了正确性。如果还是不懂的话,就只能感性的理解一下了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int n,k,a[1000],maxn,dis[100000],laste[100000],can[100000]; int next[2000000],point[100000],v[2000000],remain[2000000],cost[2000000],tot=-1; int const inf=1e9; void add(int x,int y,int z,int k) { tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; cost[tot]=k; tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; cost[tot]=-k; } int addflow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,remain[laste[now]]); now=v[laste[now]^1]; } now=t; while (now!=s) { remain[laste[now]]-=ans; remain[laste[now]^1]+=ans; now=v[laste[now]^1]; } return ans; } bool spfa(int s,int t) { memset(dis,128,sizeof(dis)); memset(can,0,sizeof(can)); can[s]=1; queue<int> p; p.push(s); dis[s]=0; while (!p.empty()) { int now=p.front(); p.pop(); can[now]=0; for (int i=point[now];i!=-1;i=next[i]) if (dis[v[i]]<dis[now]+cost[i]&&remain[i]) { dis[v[i]]=dis[now]+cost[i]; laste[v[i]]=i; if (!can[v[i]]) { can[v[i]]=1; p.push(v[i]); } } } if (dis[t]<0) return 0; maxn+=addflow(s,t)*dis[t]; return 1; } void maxflow(int s,int t) { while (spfa(s,t)); } int main() { freopen("num.in","r",stdin); freopen("num.out","w",stdout); memset(next,-1,sizeof(next)); memset(point,-1,sizeof(point)); scanf("%d%d",&n,&k); for (int i=1;i<=3*n;i++) scanf("%d",&a[i]); for (int i=1;i<=3*n;i++) add(0,i,1,0); for (int i=1;i<=3*n;i++) add(i+3*n,6*n+1,1,0); for (int i=1;i<=3*n;i++) { add(i,i+3*n,1,a[i]); for (int j=i+n;j<=3*n;j++) add(i+3*n,j,1,0); } add(6*n+1,6*n+2,k,0); maxflow(0,6*n+2); printf("%d",maxn); }