1. 程式人生 > >CF#541 D. Gourmet choice /// BFS 拓撲

CF#541 D. Gourmet choice /// BFS 拓撲

否則 答案 取出 個數 close fine 給定 con push

題目大意:

給定n m 第一行有n個數 第二行有m個數

接下來n行每行m列 有 = < >

位於 i j 的符號表示 第一行第i個數與第二行第j個數的大小關系

1.將n+m個數 當做按順序編號的點

則第二行的數是編號為 n+j 的點

2.先處理=的數 將所有=的數指向同一個父親

3.再處理不是=的數

找到兩者的父親

如果父親相同說明兩者= 與目前處理的情況相悖 無解

否則 按由小到大的關系在兩者(的父親)間連單向邊(小連向大) 此時較大的點入度+1

4.此時查看所有點的入度

入度為0 說明這個點是所有數中最小的數

將它們的答案設為1 放進隊列

5.廣搜 取出隊列裏的點

判斷是否能去向下一個點 能到的點數值比當前點大

所以 它們的答案 是 當前點的答案+1

6.查看對應點的答案 應該查詢 這個點的父親(數值相等) 的答案

因為連邊時 實際上是在對應點的父親之間連的邊

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define INF 0x3f3f3f3f
#define mem(i,j) memset(i,j,sizeof(i))
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const
int N=1e3+5; const int NN=N<<1; const int mod=1e9+7; int n, m; char G[N][N]; int E[NN][NN]; int du[NN], ans[NN]; int fa[NN]; int getfa(int x) { if(fa[x]==x) return x; else return fa[x]=getfa(fa[x]); } int main() { while(~scanf("%d%d",&n,&m)) { inc(i,1,n) scanf("%s
",G[i]+1); inc(i,1,n+m) fa[i]=i; inc(i,1,n) inc(j,1,m) // 第二行的編號為n+j if(G[i][j]===) fa[getfa(i)]=getfa(n+j); // 相等的連到同一個父親 bool OK=1; mem(E,0); mem(du,0); inc(i,1,n) inc(j,1,m) if(G[i][j]!==) { int fi=getfa(i), fj=getfa(n+j); // 父親相同說明相等 但此時是不等的情況 則無解 if(fi==fj) OK=0; else { if(G[i][j]==<&&E[fi][fj]==0) E[fi][fj]=1, du[fj]++; else if(G[i][j]==>&&E[fj][fi]==0) E[fj][fi]=1, du[fi]++; } // 由小向大連邊 並計算點的入度 } if(OK==0) { puts("No"); continue; } queue<int>q; inc(i,1,n+m) // 入度為0說明是最小的 if(du[i]==0) q.push(i), ans[i]=1; while(!q.empty()) { int u=q.front(); q.pop(); inc(i,1,n+m) if(E[u][i] && --du[i]==0) // 入度減1 即去掉u點 q.push(i), ans[i]=ans[u]+1; // 入度減為0說明沒有比他更小的了 } inc(i,1,n+m) if(du[i]) OK=0; if(OK==0) { puts("No"); continue; } puts("Yes"); inc(i,1,n) printf("%d ",ans[getfa(i)]); printf("\n"); inc(i,1,m) printf("%d ",ans[getfa(n+i)]); printf("\n"); } return 0; }
View Code

CF#541 D. Gourmet choice /// BFS 拓撲