1. 程式人生 > >【題解】 [NOI2009]變換序列 (二分圖匹配)

【題解】 [NOI2009]變換序列 (二分圖匹配)

down its -i AD out emp 二分圖 post while

懶得復制,戳我戳我

Solution:

  • 這個題面出的很毒瘤,讀懂了其實是個板子題qwq
  • 題面意思:有個\(0\)\(N-1\)的數列是由另一個數列通過加減得到的,相當於將\(A_i\)變成\(i\),每一步的代價計算就是\(min(A_i-i,N-(A_i-i))\),並且\(A_i\left(0<=i<N\right)\)互不相同,讀入代價,要求字典序最小的滿足要求的數列
  • 我們設讀入的為\(w[i]\)
  • 思路其實很簡單,\(i\)只可能是由\(i-w[i]\) 或者 \(i+w[i]\) 或者 \(i+N-w[i]\) 或者 \(i-N+w[i]\),然後我們把符合範圍\(0\)\(N-1\)
    的對應點從大到小建圖,這樣可以保證搜的時候是從小的點開始
  • 然後就從\(N-1\)\(0\)進行二分圖匹配,如果無法匹配就輸出\(No Answer\),這樣從後到前匈牙利算法去做,保證越前面的匹配的數是最小的。
  • 因為\(be[i]\)中存的是\(i\)數字對應變成的數字是什麽,所以反過來存一下輸出就好啦

    主要是想字典序最小的地方有點emmm神奇,其他的地方還是比較顯然的


Code:

//It is coded by Ning_Mew on 3.17
#include<bits/stdc++.h>

using namespace std;

const int maxn=1e5+7;

bool
vis[maxn]; int n,w[maxn],be[maxn],ans[maxn]; int head[maxn],cnt=0; struct Edge{ int nxt,to; }edge[maxn*4]; priority_queue<int>q; void add(int from,int to){ edge[++cnt].nxt=head[from]; edge[cnt].to=to; head[from]=cnt; } bool find(int k){ for(int i=head[k];i!=0;i=edge[i].nxt){ int v=edge[i].to; if
(!vis[v]){ vis[v]=true; if(be[v]==-1||find(be[v])){be[v]=k;return true;} } }return false; } int main(){ scanf("%d",&n); while(!q.empty())q.pop(); for(int i=0;i<n;i++){ scanf("%d",&w[i]); q.push(i-w[i]); q.push(i+w[i]); q.push(i+n-w[i]);q.push(i-n+w[i]); while(!q.empty()){ int box=q.top();q.pop(); //cout<<box<<endl; if(box>=0&&box<n)add(i,box);//cout<<i<<‘ ‘<<box<<endl; } } memset(be,-1,sizeof(be)); for(int i=n-1;i>=0;i--){ memset(vis,false,sizeof(vis)); if(find(i)); else{printf("No Answer\n");return 0;} } for(int i=0;i<n;i++)ans[be[i]]=i; for(int i=0;i<n;i++)printf("%d ",ans[i]);printf("\n"); return 0; }

【題解】 [NOI2009]變換序列 (二分圖匹配)