【NOI2009】Bzoj1562 & Codevs1843 變換序列

【NOI2009】Bzoj1562&Codevs1843 變換序列

Position:


List

Description

  bzoj1562

Input

輸入文件 transform.in 的第一行包含一個整數 N,表示序列的長度。接下來的
一行包含 N 個整數 D i ,其中 D i 表示 i 和 T i 之間的距離。

Output

輸出文件爲 transform.out。
如果至少存在一個滿足要求的變換序列 T,則輸出文件中包含一行 N 個整數,
表示你計算得到的字典序最小的 T;否則輸出”No Answer”(不含引號)。注意:
輸出文件中相鄰兩個數之間用一個空格分開,行末不包含多餘空格。

Sample Input

 5
1 1 2 2 1

Sample Output

1 2 4 0 3

HINT

30%的數據中 N≤50;
60%的數據中 N≤500;
100%的數據中 N≤10000。

Solution

觀察題中給的式子,發現每個位置可以填兩個數,但只能填一個數,求是否每個位置可以填上一個不重複的0~n-1的數,並保證字典序最小,也就是填在前面的數要儘量小。
最小字典序的匈牙利,貪心發現可以倒着來跑匈牙利,保證對於當前位置的數儘量小,替換掉的是後面的數,後面的數也保證了儘量小。而正着來後面的數可能會替換掉之前的數,不滿足字典序最小。

官方題解%莫隊

Transform解題報告
湖南省長郡中學 莫濤
【題目大意】
給定n和{Ti},求一個字典序最小的0..n-1的排列{Pi},使{Pi}滿足{,或指出無解。
n<=10000。
【解法分析】
 不難看出,所給限制即i與Pi的環上距離爲Ti,故對於任意i,至多存在兩個Pi(從i順時針或逆時針移動Ti距離)滿足限制。
 構建二分圖模型,以0,1..n-1爲X節點,0,1..n-1爲Y節點,若Pi=j滿足限制則由X的i節點向Y的j節點連一條邊,則問題轉化爲判斷是否存在完備匹配,若存在則求出字典序最小的匹配方案。
【算法一】
首先使用匈牙利算法求解,若不存在完備匹配則返回無解,否則按如下流程調整使得求得字典序最小解:

for do
·if i與ai,bi(ai>bi)有邊且i與ai匹配 then
·刪去匹配邊(i,ai)並暫時禁用該邊
·令j爲原來與bi匹配的點
·將i與bi匹配
·if 從j出發可以找到增廣路 then
·保留該次增廣,並在以後的增廣中禁止改變匹配邊(i,bi)
·else 還原上述改動

 該算法的核心思想即利用字典序比較的特性,從前往後嘗試改小每一位,若更改後剩餘部分存在可行解則永久保留該次更改,否則撤銷。
由於每個點至多有兩條出邊,故只在取了匹配了編號較大的點時嘗試匹配編號較小的點,而剩下部分有可行解的條件爲存在完備匹配,若直接用匈牙利算法求解則時間複雜度爲O(n^3),但我們知道:
對於任意殘量網絡,使用增廣路算法均可求得最大流(最大匹配)。
而對於任意k(k>i),k均已匹配,故只需從i出發尋找增廣路即可,這樣時間複雜度就是O(n^2)了,可以通過測試數據。
【算法二】
考慮到該圖非常特殊(X節點出度至多爲2),可以從圖論的角度求解。
首先,如果存在度爲1的點,那麼可以直接確定匹配,故可以使用隊列持續刪去度爲1的點及其確定的匹配點及於它們相關的邊,直到不存在度爲1的點。
然後,如果此時存在度爲0的解,那麼可以直接返回無解。
最後,由於所有的X節點度大於1且不大於2,故度均爲2;而所有Y節點度大於1且∑d(X)=∑d(Y),故所有Y節點度均爲2。
一個所有點度均爲2的圖必然是若干不相交的環。而一個環中只存在兩種匹配方案,直接選擇較小者即可。
該算法時間複雜度僅爲O(n)。
【算法三】
算法二雖然優秀但實現較繁瑣,不妨將其與算法一結合起來。
考慮直接使用二分圖匹配,對於算法二種在第一步刪去的點,必然能求出正確的匹配,解不優只會發生在環中。嘗試改進匹配算法:
從大到小增廣點i,尋找增廣路時優先走向編號較小的點。
證明:
1.對於任一環,匹配的最後結果由最後增廣的點決定,由上述算法的特性知,一定會找到該環內的最優解。
2.對於環外一點,若存在經過該環的增廣路,則該環內某X節點存在連向環外的邊,那麼它的度數超過2,矛盾,故不會因爲環外某點的增廣使得該環的匹配方案改變。
雖然該算法時間複雜度爲O(n^2),但思維複雜度與編程複雜度均很低,不失爲考場上的好算法。
【小結】
算法一具有普適性,其核心思想也廣泛應用於求字典序最小方案的各種問題中;算法二充分挖掘了本題中圖的特殊性,時間複雜度達到理論下界;算法三主要利用了貪心思想,非常簡潔,但其證明卻不是那麼顯然。
值得一提的是,算法三的正確性是基於該圖的特殊性的,因而缺少可推廣性,即不能解決一般二分圖的字典序最小的匹配,下圖即一個反例:
e 使用算法三將求出(1,1)(2,3)(3,2),而最優解顯然是(1,1)(2,2)(3,3)。
此外,本題還可以利用2-SAT模型求解,但該模型在各方面均不優於二分圖模型,故略去不提。

Code

// <transform.cpp> - Fri Sep 23 08:09:06 2016
// This file is made by YJinpeng,created by XuYike's black technology automatically.
// Copyright (C) 2016 ChangJun High School, Inc.
// I don't know what this program is.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define MOD 1000000007
#define INF 1e9
using namespace std;
typedef long long LL;
const int MAXN=100010;
const int MAXM=100010;
inline int max(int &x,int &y) {return x>y?x:y;}
inline int min(int &x,int &y) {return x<y?x:y;}
inline int gi() {
    register int w=0,q=0;register char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')q=1,ch=getchar();
    while(ch>='0'&&ch<='9')w=w*10+ch-'0',ch=getchar();
    return q?-w:w;
}
int n,k,t[5],match[MAXN];
vector<int>b[MAXN];bool f[MAXN];
void work(int i,int a){
    int b=i+a;
    if(b<n)t[++k]=n+b;b=i-a;
    if(b>=0)t[++k]=n+b;
}
inline void add(int v,int u){
    b[u].push_back(v);b[v].push_back(u);
}
inline bool dfs(register int x){
    if(f[x])return 0;
    int num=b[x].size();f[x]=true;
    for(int i=0;i<num;i++){
        int nex=b[x][i];
        if(match[nex]==-1||dfs(match[nex])){
            match[x]=nex;match[nex]=x;f[x]=0;return 1;
        }
    }f[x]=0;
    return 0;
}
void pri(){printf("No Answer");exit(0);}
int main()
{
    freopen("transform.in","r",stdin);
    freopen("transform.out","w",stdout);
    n=gi();
    for(int i=0;i<n;i++){
        k=0;int a=gi();work(i,a);
        work(i,n-a);
        sort(t+1,t+1+k);for(int o=1;o<=k;o++)add(i,t[o]);
    }
    for(int i=0;i<MAXN;i++)match[i]=-1;
    for(int i=n-1;i>=0;i--)
        if(match[i]==-1)if(!dfs(i))pri();
    for(int i=0;i<n-1;i++)printf("%d ",match[i]-n);
    printf("%d",match[n-1]-n);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章