当前位置: 代码迷 >> 综合 >> HDU 6094 Rikka with K-Match 【wqs二分+轮廓线dp】
  详细解决方案

HDU 6094 Rikka with K-Match 【wqs二分+轮廓线dp】

热度:69   发布时间:2023-12-06 00:12:30.0
HDU 6094 Rikka with K-Match

这不是第一次写wqs二分,只是第一次知道这玩意叫wqs二分(捂脸

f ( i ) f(i) f(i) 表示匹配了为 i i i 条边最小权值,然后发现 f ( i ) f(i) f(i) 是向上凸的。

因为 f ( i ) f(i) f(i) 也可以表示流量为 i i i 的最小费用,可以用费用流来表示的都是凸的。

然后就二分斜率 k k k ,求斜率为 k k k 时的切点。意思就是每条边的权值 ? k -k ?k ,求合法匹配的最小权值和最小权值对应的匹配的边数 p p p (多个 p p p 就取最小的)。如果 p < p< p< 我们要求的边数 n u m num num ,说明斜率 k k k 小了,反之同理,二分就行了。

最后要解决的问题就是每条边的权值 ? k -k ?k 之后,怎么求最小权值。因为没有边数的限制了,而且发现 m ≤ 4 m\le4 m4 ,轮廓线dp即可。

#include <bits/stdc++.h>
#define N 40004
#define MAX 17
using namespace std;
typedef long long ll;
const ll inf=1e17;
int T,n,m,cnt[N][5][MAX],A[N][5][2];//0横着 1竖着
ll k,dp[N][5][MAX];
void updata(int x,int y,int S,int i,int j,int T,ll co,int ad){
    ll v=dp[x][y][S]+co; int cn=cnt[x][y][S]+ad;if(v<dp[i][j][T])dp[i][j][T]=v,cnt[i][j][T]=cn;else if(v==dp[i][j][T]&&cn<cnt[i][j][T]) cnt[i][j][T]=cn;
}
ll mn,p;
bool chk(ll mid){
    int nxt,x,y; mn=inf,p=inf;for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++)for(int S=0;S<(1<<(m));S++)dp[i][j][S]=inf,cnt[i][j][S]=80000;for(int j=1;j<=m;j++){
    x=(j>1)?i:(i-1),y=(j>1)?j-1:m;for(int S=0;S<(1<<m);S++){
    if(i!=1&&(!(S&(1<<(j-1)))))nxt=S|(1<<(j-1)),updata(x,y,S,i,j,nxt,1ll*A[i-1][j][1]+mid,1);if(j!=1&&(!(S&(1<<(j-2)))))nxt=(S|(1<<(j-2)))|(1<<(j-1)),updata(x,y,S,i,j,nxt,1ll*A[i][j-1][0]+mid,1);nxt=S; if(nxt&(1<<(j-1))) nxt^=(1<<(j-1));updata(x,y,S,i,j,nxt,0,0);}}}for(int S=0;S<(1<<(m));S++)if(dp[n][m][S]<mn||(dp[n][m][S]==mn&&cnt[n][m][S]<p)) mn=dp[n][m][S],p=cnt[n][m][S];return p<=k;
}
void work(){
    scanf("%d %d %lld",&n,&m,&k);for(int i=1;i<n;i++)for(int j=1;j<=m;j++) scanf("%d",&A[i][j][1]);for(int i=1;i<=n;i++)for(int j=1;j<m;j++) scanf("%d",&A[i][j][0]);ll r=1e14,l=0,mid;while(l<r){
    mid=(l+r)/2+1;if(chk(-mid))l=mid;else r=mid-1;}chk(-l);printf("%lld\n",mn+k*l);
}
int main(){
    
// freopen("test.in","r",stdin);scanf("%d",&T);while(T--) work();
}
  相关解决方案