[bzoj1562][NOI2009]变换序列——匈牙利or环套树 大佬们的博客 Some Links

版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/82052992

题目大意:

有一个n的排列P ,定义D(x, y) = min(|x − y|, n − |x − y|),给
出D(i, P i ),求出满足条件的字典序最小的P ,或者判断无解。
n ≤ 10 4 .

思路:

显然是是一个二分图匹配,但是题目要求满足字典序最小,考虑每一个点加边从小到达加,先加终点的编号小的边。
按照上述方式跑匈牙利,如果点从编号小的往大的跑增广路,会出现后面的点把前面的编号小的匹配给占用掉而导致答案不更优,所以反过来跑匈牙利即可。
匈牙利是 n m 的,这一题因为常数较小所以可以跑过去,其实有环套树的线性做法。
每一个下标显然有且仅有两个数字满足条件,设这两个数字为u,v,则在图中连接u,v,则转化为了每一条边只可以选择一个顶点,每个顶点只可以被一条边选择,从而发现题目有解必须每一个连通块都是一个环套树,直接在环套树上贪心选取即可。
匈牙利做法

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj1562.in","r",stdin);
    freopen("bzoj1562.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=10000+10;
int n,d[maxn],to[maxn][2],be[maxn],ans[maxn],cnt;
bool vis[maxn];

bool Hungary(int u){
    REP(i,0,1){
        int v=to[u][i];
        if(vis[v])continue;
        vis[v]=1;
        if(!be[v] || Hungary(be[v])){
            be[v]=u;
            return true;
        }
    }
    return false;
}

void init(){
    read(n);
    REP(i,1,n)read(d[i]);
    REP(i,1,n){
        int num1=i-d[i],num2=i+d[i];
        if(num1<=0)num1+=n; if(num1>n)num1-=n;
        if(num2<=0)num2+=n; if(num2>n)num2-=n;
        if(num1>num2)swap(num1,num2);
        to[i][0]=num1; to[i][1]=num2;
    }
}

void work(){
    DREP(i,n,1){
        memset(vis,0,sizeof(vis));
        cnt+=Hungary(i);
    }
    if(cnt!=n){puts("No Answer");return;}
    REP(i,1,n)ans[be[i]]=i;
    REP(i,1,n)printf("%d ",ans[i]-1);
}

int main(){
//  File();
    init();
    work();
    return 0;
}

环套树做法

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj1562.in","r",stdin);
    freopen("bzoj1562.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=10000+10;
const int inf=0x3f3f3f3f;
int n,d[maxn],be[maxn];
int beg[maxn],las[maxn<<1],to[maxn<<1],w[maxn<<1],cnte=1,ans[maxn];

void add(int u,int v,int num){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; w[cnte]=num;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; w[cnte]=num;
}

void init(){
    read(n);
    REP(i,1,n)read(d[i]);
    REP(i,1,n){
        int num1=i-d[i],num2=i+d[i];
        if(num1<=0)num1+=n; if(num1>n)num1-=n;
        if(num2<=0)num2+=n; if(num2>n)num2-=n;
        add(num1,num2,i);
    }
}

stack<int>stk;
vector<int>c[maxn];
bool vis[maxn],in[maxn];
int flag;

void dfs(int u,int fr){
    if(vis[u]){
        if(flag)return;
        flag=u;
        for(int p=0;to[p^1]!=u;stk.pop()){
            p=stk.top();
            c[u].push_back(p);
            in[to[p]]=1;
        }
        return;
    }
    vis[u]=1;
    for(int i=beg[u];i;i=las[i]){
        if(i==(fr^1))continue;
        stk.push(i);
        dfs(to[i],i);
        if(!flag)stk.pop();
    }
}

void dfs_tree(int u,int f){
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==f || in[to[i]])continue;
        ans[w[i]]=to[i];
        dfs_tree(to[i],u);
    }
}

void dfs_circle(int u,int fr,int rt){
    for(int i=beg[u];i;i=las[i]){
        if(!in[to[i]])continue;
        if(i==(fr^1) || to[i]==rt)continue;
        ans[w[i]]=to[i];
        dfs_circle(to[i],i,rt);
    }
}

void work(){
    REP(i,1,n)if(!vis[i]){
        flag=0;
        while(!stk.empty())stk.pop();
        dfs(i,0);
        if(!flag){puts("No Answer");return;}
        int Min=inf,num=0;
        for(int sz=c[flag].size()-1,j=0;j<=sz;++j){
            dfs_tree(to[c[flag][j]],0);
            if(w[c[flag][j]]<Min)Min=w[c[flag][j]],num=c[flag][j];
        }
        if(to[num]<to[num^1]){
            ans[w[num]]=to[num];
            dfs_circle(to[num],num,to[num]);
        }
        else{
            ans[w[num]]=to[num^1];
            dfs_circle(to[num^1],num^1,to[num^1]);
        }
    }
    REP(i,1,n)printf("%d ",ans[i]-1);
}

int main(){
    File();
    init();
    work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/82052992
今日推荐