小朋友(洛谷-P3852)
題目描述
幼兒園里有N個(gè)小朋友,老師要從中選出來(lái)一部分做丟手絹的游戲,可是老師沒(méi)有想到這么小的孩子里面有些人之間還有矛盾。老師想找出盡量多的小朋友去玩游戲,但是又很頭疼,他不想看到找出來(lái)玩游戲的小朋友里面還有任何兩個(gè)人之間存在著矛盾。如果告訴你小朋友之間存在的M對(duì)矛盾關(guān)系,你能否幫助幼兒園老師計(jì)算出他最多可以選出多少個(gè)小朋友來(lái)做這個(gè)丟手絹的游戲?
關(guān)于矛盾限制的說(shuō)明:
如果我們把存在著矛盾的兩個(gè)小朋友看作是無(wú)向圖中相連的兩個(gè)點(diǎn),那么題目中的數(shù)據(jù)保證M對(duì)矛盾所構(gòu)成的圖中不會(huì)有超過(guò)3個(gè)點(diǎn)的環(huán)。(圖1符合要求,圖2則不符合)
輸入輸出格式
輸入格式:
輸入文件的第一行是用空格隔開(kāi)的兩個(gè)整數(shù)N和M,表示一共有N個(gè)小朋友,這些小朋友之間有M對(duì)矛盾關(guān)系。接下來(lái)的M行,每行將有一對(duì)整數(shù)a和b(用空格隔開(kāi)),表示小朋友a(bǔ)與小朋友b有矛盾。(小朋友的編號(hào)都是從1開(kāi)始的)
輸出格式:
輸出一行,包含一個(gè)整數(shù),即幼兒園老師最多可以選出來(lái)做游戲的人數(shù)。
輸入輸出樣例
輸入樣例#1:
5 6
1 2
3 2
1 3
3 5
3 4
4 5
輸出樣例#1:
2
說(shuō)明
幼兒園有6個(gè)小朋友,矛盾關(guān)系中1 - 2 - 3組成一個(gè)環(huán),3 - 4 - 5組成一個(gè)環(huán),因此只能在這兩個(gè)環(huán)中分別選一個(gè)小朋友,并且不能選擇3號(hào)小朋友。
對(duì)于20%的數(shù)據(jù),1 ≤ N ≤ 20
對(duì)于40%的數(shù)據(jù),1 ≤ N ≤ 50
對(duì)于100%的數(shù)據(jù),1 ≤ N ≤ 200
對(duì)于100%的數(shù)據(jù)都符合題目中所描述的矛盾限制關(guān)系。
思路:可以看出,給出的圖是一個(gè)弦圖,本質(zhì)上是要求一個(gè)最大獨(dú)立集,套用 MCS 后貪心選取完美消除序列即可
源代碼
#include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<utility> #include<stack> #include<queue> #include<vector> #include<set> #include<map> #include<bitset> #define PI acos(-1.0) #define INF 0x3f3f3f3f #define LL long long #define Pair pair<int,int> LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; } LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; } LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); } const double EPS = 1E-10; const int MOD = 1E9+7; const int N = 500; const int dx[] = {-1,1,0,0,-1,-1,1,1}; const int dy[] = {0,0,-1,1,-1,1,-1,1}; using namespace std;struct Edge {int to,next;Edge() {}Edge(int to,int next):to(to),next(next) {} }; struct MCS{Edge edge[N*N];int head[N],tot;int n,m;bool vis[N];int id[N];//編號(hào)int label[N];//與多少標(biāo)號(hào)點(diǎn)相鄰int order[N];//完美消除序列vector<int> V[N];void init(int n,int m) {this->n=n;this->m=m;for(int i=1; i<=n; i++)V[i].clear();memset(head,-1,sizeof(head));memset(order,0,sizeof(order));memset(label,0,sizeof(label));memset(vis,0,sizeof(vis));memset(id,0,sizeof(id));tot=0;}void addEdge(int x,int y) {edge[tot].to=y;edge[tot].next=head[x];head[x]=tot++;}void mcs() {for(int i=1; i<=n; i++)V[0].push_back(i);int maxx=0;int now=0;for(int i=1; i<=n; i++) { //從前往后bool flag=false;while(!flag) {for(int j=V[maxx].size()-1; j>=0; j--) { //從后往前if(vis[V[maxx][j]])V[maxx].pop_back();else {flag=true;now=V[maxx][j];break;}}if(!flag)maxx--;}vis[now]=true;//逆序存放order[n-i+1]=now;id[now]=n-i+1;for(int j=head[now]; j!=-1; j=edge[j].next) {int to=edge[j].to;if(!vis[to]) {label[to]++;V[label[to]].push_back(to);maxx=max(maxx,label[to]);}}}}int bucket[N];//桶int judge() { //判斷是否是弦圖memset(vis,0,sizeof(vis));memset(bucket,0,sizeof(bucket));for(int i=n; i>0; i--) {int cnt=0;int ret=1;for(int j=head[order[i]]; j!=-1; j=edge[j].next)if(id[edge[j].to]>i)vis[bucket[++cnt]=edge[j].to]=1;for(int j=head[bucket[1]]; j!=-1; j=edge[j].next) {int to=edge[j].to;if(to!=bucket[1]&&vis[to]) {if(vis[to]) {ret++;vis[to]++;}}}for(int j=1; j<=cnt; j++)vis[bucket[j]]=0;if(cnt&&ret!=cnt)return false;}return true;}int getMaximumClique() { //計(jì)算最大團(tuán)、最小著色int res=0;for(int i=1; i<=n; i++)res=max(res,label[i]+1);return res;}int getMaximumIndependentSet() { //計(jì)算最大獨(dú)立集、最小團(tuán)覆蓋memset(vis,0,sizeof(vis));int res=0;for(int i=1; i<=n; i++) {if(!vis[order[i]]) {res++;vis[order[i]]=true;for(int j=head[order[i]]; j!=-1; j=edge[j].next)vis[edge[j].to]=true;}}return res;} }mcs; int main() {int n,m;scanf("%d%d",&n,&m);mcs.init(n,m);for(int i=1; i<=m; i++) {int x,y;scanf("%d%d",&x,&y);mcs.addEdge(x,y);mcs.addEdge(y,x);}mcs.mcs();int res=mcs.getMaximumIndependentSet();//最大獨(dú)立集、最小團(tuán)覆蓋printf("%d\n",res);return 0; }?
總結(jié)
以上是生活随笔為你收集整理的小朋友(洛谷-P3852)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++语言基础 —— STL —— 容器
- 下一篇: 不降的数字(51Nod-2499)