hdu2236無題II(二分圖修改+二分+列舉)
阿新 • • 發佈:2018-12-24
連結:http://acm.hdu.edu.cn/showproblem.php?pid=2236
題目意思:在一個n*n的矩陣中,找n個數,使得這n個數在不同行不同列(類似於八皇后,但是不用管對角線),並且要 求這n個數的最大最小值的差值最小。
題目分析:
類似於八皇后的不同行不同列一開始我想到了dfs,但是100的資料而且還沒有對角線的剪紙肯定超時吧,23333,所以這題二分圖匹配的話就很巧妙了。為什麼會想到用二分圖呢?仔細想一下,如果某點(x,y),被選中,那麼橫座標上x的值不能再選,縱座標上的y值也不能再選,這相當於二維上有很多值,但是每個值都只能用一次,這就可以想到用二分圖匹配求完美匹配了。
想到用二分圖匹配後,難點是求n個數的最大最小的差值最小。如果常規的想解決這個問題,無非就是找到每次匹配後的最大最小值,但是存在的一個問題是一般我們只要找到一個最大匹配就行了,找出所有的完美匹配還是困難的呢,各種跡象表明,這樣是會超時的。這裡求差值最小,其實是一個二分答案的套路應用吧:,首先這個差值一定是0-(maxv-minv)之間的,然後我們二分差值為外迴圈,列舉下界為內迴圈,得到一個區間。修改二分圖匹配模板:如果這個二分圖的匹配點是在這個區間裡的前去匹配,hungry()函式中返回是否為完美匹配,對於每一個二分的差值,只要它能找到一個滿足區間條件的完美匹配,就記錄一下答案。
程式碼:
#include<cstdio> #include<string.h> #include<algorithm> using namespace std; const int maxn =333; const int inf=0x3f3f3f3f; int g[maxn][maxn]; int n; int maxv,minv; int l,r,mid; int linker[maxn]; int used[maxn]; int p; int dfs(int x){ for(int i=1;i<=n;i++){ if(p<=g[x][i]&&g[x][i]<=p+mid&&!used[i]){//***界定二分,模板修改核心 used[i]=1; if(linker[i]==-1||dfs(linker[i])){ linker[i]=x; return 1; } } } return 0; } int hungry(void){ int res=0; memset(linker,-1,sizeof(linker)); for(int i=1;i<=n;i++){ memset(used,0,sizeof(used)); if(dfs(i))res++; } return res==n?1:0;//***模板修改 } int main(){ int t; scanf("%d",&t); while(t--){ maxv=-inf; minv=inf; scanf("%d",&n); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ scanf("%d",&g[i][j]); maxv=max(maxv,g[i][j]); minv=min(minv,g[i][j]); } } l=0; r=maxv-minv; int res=0; while(l<=r){ int f=0; mid=(l+r)/2; for(p=minv;p+mid<=maxv;p++){//列舉下屆確定範圍 if(hungry()){ f=1; break; } } if(f){res=mid;r=mid-1;} else l=mid+1; } printf("%d\n",res); } }