「NOIP Record」#17 二分图判定

二分图判定

没啥技巧,最难的是把图论模型建起来。

luogu1330 封锁阳光大学

相邻两个点只能封锁一个,但是要覆盖所有边。对应到二分图上就是左部右部点的数量取较小值。

图可能不连通,取的是每一张二分图的左右边的较小值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define PII pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define SET(a,b) memset(a,b,sizeof(a))
#define CPY(a,b) memcpy(a,b,sizeof(b))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
    int a=0, f=1; char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) a=a*10+c-'0', c=getchar();
    return a*f;
}
const int N=1e4+5;
int n, m, c[N], deg[N];
vector<int> p[N];
int c1, c2;
void add(int x,int y) {
    p[x].pb(y), p[y].pb(x);
}
bool dfs(int x,int col) {
    c[x]=col;
    if(col==1) ++c1; else ++c2;
    bool res=1;
    for(auto y:p[x]) {
        if(!c[y]) res&=dfs(y,3-col);
        else res&=(c[y]!=col);
    }
    return res;
}
signed main() {
    n=read(), m=read();
    rep(i,1,m) {
        int x=read(), y=read();
        ++deg[x], ++deg[y];
        add(x,y);
    }
    int ans=0;
    rep(i,1,n) if(!c[i]) {
        c1=c2=0;
        if(!dfs(i,1)) { puts("Impossible"); return 0; }
        ans+=min(c1,c2);
    }
    printf("%lld\n",ans);
    return 0;
}

luogu1155 [NOIP2008 提高组] 双栈排序

考虑什么情况下,两个元素不能在用一个栈中。注意不一定是同时,先后进栈也算。

不难发现 $i,j$ 不能在同一个栈中,当且仅当存在 $(i,j,k)$,满足 $i<j<k$,$a_k <a_i < a_j$。

考虑在 $i,j$ 之间连边,表示二者不能用同一个栈,那么有解当且仅当这张图是二分图。

考虑如何构造方案。

对图黑白染色,编号小的点贪心染白色,同时规定第一个栈是白色栈。

维护一个全局变量 $now$ 表示下一个应该放哪个元素,从 $1$ 到 $n$ 扫一遍,对于一个将要加入的 $a_i$,维护它所在颜色的栈中元素单调递增,同时判断栈顶是否是 $now$,如果不是就输出另一个栈的栈顶。

由于白色栈的操作优先级都要高于黑色栈,所以在加入 $a_i$ 之前,应该输出白栈的栈顶直到不是 $now$。

最后输出剩下的元素,贪心白色栈即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define PII pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define SET(a,b) memset(a,b,sizeof(a))
#define CPY(a,b) memcpy(a,b,sizeof(b))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
    int a=0, f=1; char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) a=a*10+c-'0', c=getchar();
    return a*f;
}
const int N=1005;
int n, now=1, a[N], suf[N], c[N];
vector<int> p[N];
void add(int x,int y) {
    p[x].pb(y), p[y].pb(x);
}
bool dfs(int x,int col) {
    c[x]=col;
    bool res=1;
    for(auto y:p[x]) {
        if(!c[y]) res&=dfs(y,3-col);
        else res&=(c[y]!=col);
        if(!res) return 0;
    }
    return 1;
}
stack<int> s[3];
bool check(int col) {
    return s[col].size()&&s[col].top()==now;
}
void out(int col) {
    s[col].pop();
    ++now;
    if(col==1) printf("b ");
    else printf("d ");
}
signed main() {
    n=read();
    rep(i,1,n) a[i]=read();
    suf[n]=a[n];
    per(i,n-1,1) suf[i]=min(suf[i+1],a[i]);
    rep(i,1,n) rep(j,i+1,n-1) {
        if(suf[j+1]<a[i]&&a[i]<a[j]) add(i,j);
    }
    rep(i,1,n) if(!c[i]) {
        if(!dfs(i,1)) { puts("0"); return 0; }
    }
    rep(i,1,n) {
        while(s[c[i]].size()&&s[c[i]].top()<=a[i]) {
            if(check(c[i])) out(c[i]);
            else out(3-c[i]);
        }
        while(check(1)) out(1);
        s[c[i]].push(a[i]);
        if(c[i]==1) printf("a "); else printf("c ");
    }
    while(s[1].size()||s[2].size()) {
        if(check(1)) out(1);
        else if(check(2)) out(2);
    }
    return 0;
}

luogu1285 队员分组

老题,从其他 OJ 扒来的换皮题,但是质量意外的高啊。

预处理出所有不互相认识的人,他们一定不能在同一组。在他们之间连边,发现如果有解当且仅当连出的图是二分图。

题目要求最小化两组人数之差,但是多个连通块一起考虑显然很难做。这时候可以利用连通块之间相对独立的性质,对每个连通块单独考虑,同时只有单独一个连通块的分组方法是确定的。

这时候我们能发现,每个连通块有两种颜色,每个颜色的所有点都必须一起选择,但是不同连通块的不同颜色却可以分到同一组。因此问题转化为分组背包。

设 $f(i,j)$ 为考虑前 $i$ 个连通块,能否选择 $j$ 个点。转移是平凡的,同时记录到达 $f(i,j)$ 的最后一个决策是选择的哪个颜色。

设连通块个数为 $m$。

分成两组,必然有一组人数不超过 $n / 2$。找到 $f(m,i)$ 中满足 $j \le n/2$ 且 $f(n,j)=1$ 的极大的 $j$,这就是人数较少那组的人数了。

此时我们就可以利用记录的决策输出方案了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define PII pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define SET(a,b) memset(a,b,sizeof(a))
#define CPY(a,b) memcpy(a,b,sizeof(b))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
    int a=0, f=1; char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) a=a*10+c-'0', c=getchar();
    return a*f;
}
const int N=105;
int n, a[N][N], col[N];
int cnt;
vector<int> p[N], con[N][2], ans[2];
int f[N][N], op[N][N];
void add(int x,int y) {
    p[x].pb(y), p[y].pb(x);
}
void input() {
    n=read();
    rep(i,1,n) {
        int j;
        do {
            j=read();
            a[i][j]=1;
        } while(j);
    }
}
void build() {
    rep(i,1,n) rep(j,1,n) if(i!=j) {
        if(!a[i][j]||!a[j][i]) add(i,j);
    }
}
bool dfs(int x,int color) {
    col[x]=color;
    con[cnt][col[x]].pb(x);
    int res=1;
    for(auto y:p[x]) {
        if(~col[y]) res&=(col[y]!=col[x]);
        else res&=dfs(y,col[x]^1);
        if(!res) return 0;
    }
    return 1;
}
void getcol() {
    SET(col,-1);
    rep(i,1,n) if(col[i]<0) {
        ++cnt;
        if(!dfs(i,0)) { puts("No solution"); exit(0); }
    }
}
#define sz(x) con[i][x].size()
void getans(int i,int j) {
    if(i<1) return;
    for(auto x:con[i][op[i][j]]) ans[0].pb(x);
    for(auto x:con[i][op[i][j]^1]) ans[1].pb(x);
    getans(i-1,j-sz(op[i][j]));
}
void dp() {

    f[0][0]=1;
    rep(i,1,cnt) rep(j,0,n) {
        if(j>=sz(0)&&f[i-1][j-sz(0)]) f[i][j]=1, op[i][j]=0;
        if(j>=sz(1)&&f[i-1][j-sz(1)]) f[i][j]=1, op[i][j]=1;
        // 都可以的话0/1随便取一个
    }
    int j=0;
    for(int i=n/2;i;--i) if(f[cnt][i]) { j=i; break; }
    getans(cnt,j);

}
void output() {
    sort(ans[0].begin(),ans[0].end());
    sort(ans[1].begin(),ans[1].end());
    // 注意输出要排序
    printf("%lld ",(int)ans[0].size());
    for(auto x:ans[0]) printf("%lld ",x);
    puts("");
    printf("%lld ",(int)ans[1].size());
    for(auto x:ans[1]) printf("%lld ",x);
}
signed main() {
    input();
    build();
    getcol();
    dp();
    output();
    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇