zoukankan      html  css  js  c++  java
  • 「HDU6094」Rikka with K-Match

    题目链接

    题目大意

    (n imes m) 的网格图 要求你求一个大小为 (k) 的最小权匹配。

    (n leq 40000,m leq 4,k leq frac{nm}{2})

    多组数据 数据组数 (T leq 1000),保证只有三组数据 (n > 100)

    题解

    首先这个题是可以二分图染色跑费用流的。所以设 (w_i) 表示 (i) 条边的答案。很容易有 (w_i-w_{i-1} leq w_{i+1}-w_i) (跑的是最小费用流 越往后增加一次费用肯定越多)

    所有能建出费用流的问题基本上都是凸的

    所以我们可以先 wqs 二分一发去掉大小的限制。

    然后就是一个经典问题:网格图最小权匹配。写个轮廓线 dp 就行了。

    dp 就设 (f_{i,j,S}) 表示考虑到((i,j)) 这个点 它上面的轮廓线的状态是 (S) 转移的时候看看这个点左边的和上边的边是否选就好了。

    但是插头 dp 很难调(对我来说),所以我说下我的错误:

    1. 边界情况 特别是在换行的时候 因为我是把多出来的放在最后一位 换行的时候需要操作一下
    2. 不要漏掉边 我一开始写的程序漏掉了最上面的边
    3. wqs二分边界:这个题斜率的取值范围到 (frac{nm}{2}10^9) 而不是 (10^9),因为考虑在增广过程中一条 (1)(10^9)的交错链 增广一次会让所有的 (1) 都切换到 (10^9) 会增加好多。(所以 wqs 二分看边界要看原问题的最大斜率 而不是感性理解)
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    
    #define fi first
    #define se second
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(register int i = a;i <= b;++i)
    #define ROF(i,a,b) for(register int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 4e4+5;
    const int MAXM = 5;
    int R[MAXN][MAXM],D[MAXN][MAXM];
    int n,m,k;
    LL f[2][(1<<MAXM)+2],inf;
    int g[2][(1<<MAXM)+2],now;
    
    inline char nc(){
    	#define SIZE 1000000+3
    	static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
    	if(p1 == p2){
    		p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
    		if(p1 == p2) return -1;
    	}
    	return *p1++;
    	#undef SIZE
    }
    
    template <typename T>
    inline void read(T &x){
    	x = 0;int flag = 0;char ch = nc();
    	while(!isdigit(ch)){
    		if(ch == '-') flag = 1;
    		ch = nc();
    	}
    	while(isdigit(ch)){
    		x = (x<<1) + (x<<3) + (ch^'0');
    		ch = nc();
    	}
    	if(flag) x = -x;
    }
    
    inline int chk(LL ext){
    	CLR(f[0],0x3f);CLR(g[0],0);now = 0;
    	inf = f[0][0];
    	f[0][0] = 0;
    	FOR(i,0,n-1){
    		FOR(j,0,m-1){
    			CLR(f[now^1],0x3f);CLR(g[now^1],0);
    			FOR(S,0,(1<<(m+1))-1){
    				if(f[now][S] == inf) continue;
    				// 竖着的边
    				if(i != 0 && !((S>>j)&1) && !((S>>m)&1)){// !优先级很高 放在最外边
    					int nxt = S;nxt |= (1<<j);
    					if((nxt>>(j+1))&1) nxt ^= (1<<(j+1)),nxt |= (1<<m);
    					LL gx = f[now][S]+D[i-1][j]+ext;
    					if(f[now^1][nxt] > gx || (f[now^1][nxt] == gx && g[now^1][nxt] < g[now][S]+1)){
    						f[now^1][nxt] = gx;g[now^1][nxt] = g[now][S]+1;
    					}
    				}
    				// 横着的边
    				if(j != 0 && !((S>>j)&1) && !((S>>(j-1))&1)){
    					int nxt = S;nxt |= (1<<j);nxt |= (1<<(j-1));
    					if((nxt>>m)&1) nxt ^= (1<<m);
    					if((nxt>>(j+1))&1) nxt ^= (1<<(j+1)),nxt |= (1<<m);
    					LL gx = f[now][S]+R[i][j-1]+ext;
    					if(f[now^1][nxt] > gx || (f[now^1][nxt] == gx && g[now^1][nxt] < g[now][S]+1)){
    						f[now^1][nxt] = gx;g[now^1][nxt] = g[now][S]+1;
    					}
    				}
    				// 啥都不用
    				int nxt = S;
    				if((nxt>>m)&1) nxt ^= (1<<m);
    				if((nxt>>(j+1))&1) nxt ^= (1<<(j+1)),nxt |= (1<<m);
    				LL gx = f[now][S];
    				if(f[now^1][nxt] > gx || (f[now^1][nxt] == gx && g[now^1][nxt] < g[now][S])){
    					f[now^1][nxt] = gx;g[now^1][nxt] = g[now][S];
    				}
    			}
    			now ^= 1;
    		}
    		CLR(f[now^1],0x3f);CLR(g[now^1],0);
    		FOR(S,0,(1<<(m+1))-1){
    			int nxt = S;if((nxt>>m)&1) nxt ^= (1<<m);
    			if(nxt&1) nxt--,nxt |= (1<<m);
    			LL gx = f[now][S];
    			if(f[now^1][nxt] > gx || (f[now^1][nxt] == gx && g[now^1][nxt] < g[now][S])){
    				f[now^1][nxt] = gx;g[now^1][nxt] = g[now][S];
    			}
    		}
    		now ^= 1;
    	}
    	LL mn = inf;int ps = -1;
    	FOR(S,0,(1<<(m+1))-1){
    		if(mn > f[now][S] || (mn == f[now][S] && ps < g[now][S])){
    			mn = f[now][S];ps = g[now][S];
    		}
    	}
    	return ps;
    }
    
    inline void Solve(){
    	read(n);read(m);read(k);
    	FOR(i,0,n-2) FOR(j,0,m-1) read(D[i][j]);
    	FOR(i,0,n-1) FOR(j,0,m-2) read(R[i][j]);
    	LL l = -1e14,r = 0,ans = 0;
    	while(l <= r){
    		LL mid = (l + r) >> 1;
    		if(chk(mid) >= k) ans = mid,l = mid+1;
    		else r = mid-1;
    	}
    	chk(ans);
    	LL mn = inf;int ps = -1;
    	FOR(S,0,(1<<(m+1))-1){
    		if(mn > f[now][S]){
    			mn = f[now][S];ps = g[now][S];
    		}
    	}
    	printf("%lld
    ",mn-k*ans);
    }
    
    int main(){
    	int T;read(T);
    	while(T--) Solve();
    	return 0;
    }
    

    [collapse status="false" title="写完了再看"]这题可能是个毒瘤卡常题+轮廓线题 所以要留下充裕的时间写[/collapse]

  • 相关阅读:
    如何删除或更改已经释放的TR
    [问题解决]调用BAPI_ACC_DOCUMENT_POST时报错“被合并的公司 XXXX 和 XXXX 是不同的”
    如何判断暂存采购订单(EKKO-MEMORY)
    Django之HttpRequest和HttpReponse
    Django之模板继承
    Django之模板语法
    python库之selectors
    python库之threading
    JDBC学习笔记(9)——DBUtils的使用
    XML学习笔记(1)--XML概述
  • 原文地址:https://www.cnblogs.com/rainair/p/14471631.html
Copyright © 2011-2022 走看看