@darkproject
2017-03-30T18:03:44.000000Z
字数 3522
阅读 860
集训队
A - COURSES (POJ - 1469)
题意大致是P门课n个学生,学生可以选一门课程或者不选,但要求每门课程都要被选上并且是不同的学生代表。
我们可以将学生与课程二分建图,判断其最大匹配数是否满足条件,满足即为yes
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
const int maxn = 405;
using namespace std;
int un, vn, g[maxn][maxn];
int linker[maxn], check[maxn];
bool dfs(int u) {
int v;
for (v = 1; v<=vn; v++)
{
if (g[u][v] && !check[v])
{
check[v] = 1;
if (linker[v] == -1 || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
}
int hungarian()
{
int ans = 0;
memset(linker, -1, sizeof(linker));
for (int u = 1; u<=un; u++)
{
memset(check, 0, sizeof(check));
if (dfs(u)) ans++;
}
return ans;
}
int main()
{
int p, n, a, b, t;
scanf("%d", &t);
while (t--)
{
memset(g,0,sizeof(g));
scanf("%d%d", &p, &n);
un = p, vn = n;
for (int i = 1; i<=p; ++i)
{
scanf("%d", &a);
for (int j = 0; j<a; j++)
{
scanf("%d", &b);
g[i][b] = 1;
}
}
if (hungarian()==p) printf("YES\n");
else printf("NO\n");
}
return 0;
}
B - The Accomodation of Students HDU - 2444
N个学生他们可能认识也可能不认识,现考虑能否将他们分为2组,使2组的学生都是熟人,每组内的学生都是陌生人,如果可以分成2组算出需要多少房间,不可以则输出No
ps:判断是否为二分图:选取某个点作为起点并染为某种颜色、同时把它相邻的元素染为对立的颜色,进行搜索,如果到那步发现当前点和相邻点的颜色一样,那么就出现了矛盾,就不是二分图。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
const int maxn=205;
using namespace std;
typedef vector<int>::iterator mxr;
int n,m,color[maxn],check[maxn],linker[maxn];
//vector<int>un;
vector<int>G[maxn];
bool judge(int v,int c)
{
color[v]=c;
for(int i=0;i<G[v].size();i++)
{
if(color[G[v][i]]==c) return false;
if(color[G[v][i]]==0&&!judge(G[v][i],-c)) return false;
}
return true;
}
bool solve()
{
memset(color,0,sizeof(color));
for(int i=0;i<n;++i)
if(color[i]==0)
if(!judge(i,1))
return false;
return true;
}
bool dfs(int u)
{
int v;
for(mxr i=G[u].begin();i!=G[u].end();i++)
{
v=*i;
if(!check[v])
{
check[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{
linker[v]=u;
return true;
}
}
}
return false;
}
int hungarian()
{
int ans=0;
memset(linker,-1,sizeof(linker));
//mxr it;
//for(it=un.begin();it!=un.end();++it)
for(int i=1;i<=n;i++)
{
memset(check,0,sizeof(check));
if(dfs(i)) ans++;
}
return ans;
}
void init()
{
//un.clear();
for(int i=0;i<maxn;i++)
G[i].clear();
}
int main()
{
int x,y;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=0;i<m;++i)
{
scanf("%d%d",&x,&y);
// un.push_back(x);
G[x].push_back(y);
}
if(!solve())
printf("No\n");
else
{
printf("%d\n",hungarian());
}
}
return 0;
}
C - Guardian of Decency POJ - 2771
老师带学生出去旅游,但是担心学生会发生恋爱关系,所以带出去的学生至少要满足以下要求之中的一个:
1.身高相差40cm以上
2.同性
3.喜欢的音乐风格不同
4.喜欢的运动相同
问最多可以带出去多少学生?
最大独立集:最大的一个集合,其中的每两点之间都不存在边
最大独立集=顶点数-最大匹配数
ps:因为dfs扫点扫了2遍,所以最大匹配数/2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <cstring>
const int maxn = 505;
using namespace std;
int check[maxn], linker[maxn];
int map[maxn][maxn];
int t, n;
struct mxr
{
int hei;
char sex;
char music[105];
char sport[105];
}stu[maxn];
bool judge(mxr a, mxr b)
{
if (a.sex != b.sex&&strcmp(a.music, b.music) == 0 && abs(a.hei - b.hei) <= 40 && strcmp(a.sport, b.sport) != 0)
return true;
return false;
}
bool dfs(int u)
{
for (int v = 0; v<n; v++)
{
if (map[u][v] && !check[v])
{
check[v] = 1;
if (linker[v] == -1 || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
}
int hungarian()
{
int ans = 0;
memset(linker, -1, sizeof(linker));
for (int i = 0; i<n; i++)
{
memset(check, 0, sizeof(check));
if (dfs(i)) ans++;
}
return ans;
}
int main()
{
scanf("%d", &t);
while (t--)
{
memset(map, 0, sizeof(map));
scanf("%d", &n);
for (int i = 0; i<n; ++i)
scanf("%d %c %s %s", &stu[i].hei, &stu[i].sex, stu[i].music,stu[i].sport);
for (int i = 0; i<n; ++i)
for (int j = 0; j<n; j++)
{
if (judge(stu[i], stu[j]))
map[i][j] = 1;
}
printf("%d\n", n - hungarian()/2);
}
return 0;
}
自补:
codeforce #round 400 (div1+div2) D. The Door Problem
m个开关控制n个门,每个门由2个开关控制。门的状态由0,1表示,问任意按下开关是否可以使所有的门为0状态。此题可以用并查集或者二分图做,将开关视为点,门的状态作为点边连接。开关分为2个状态按与不按,可以将其分为i+m与i来表示这个状态,最后判断是否在一个并查集里同时存在i+m与m即可,若同时存在即为NO。二分图将开关按下与不按下分为2个集合然后dfs判断图中是否存在环,若存在即为NO。