题目链接
题意:
一个棋盘,其中某些区域是障碍物不能放东西,然后所放的物品必须满足不能在两者之前没有障碍物的情况下,位于同一行、或者同一列
题解:
这题我并没有用建二分图,而是直接bfs
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+10;
int n;
char mp[10][10];
int maxnum;
int check(int x,int y){
if(mp[x][y]!='.')
return 0;
for(int i=x-1;i>=0;i--){
if(mp[i][y]=='B'){
return 0;
}
else if(mp[i][y]=='X'){
break;
}
}
for(int i=y-1;i>=0;i--){
if(mp[x][i]=='B'){
return 0;
}
else if(mp[x][i]=='X'){
break;
}
}
return 1;
}
void dfs(int pos,int cnt){
if(pos==n*n){
maxnum=max(maxnum,cnt);
return ;
}
int x=pos/n;
int y=pos%n;
if(check(x,y)){
mp[x][y]='B';
dfs(pos+1,cnt+1);
mp[x][y]='.';
}
dfs(pos+1,cnt);
return ;
}
int main(){
while(~scanf("%d",&n)){
if(n==0)
break;
for(int i=0;i<n;i++){
scanf("%s",mp[i]);
}
maxnum=0;
dfs(0,0);
cout<<maxnum<<endl;
}
return 0;
题目链接
题解:
首先判断判断是不是二分图,如果不是输出No
如果是的话,再进行二分图匹配,直接套匈牙利算法即可,
但是tle了好几次,数组开小了。。。。
代码参考的这篇博客:
https://blog.csdn.net/u014422052/article/details/43604927?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=10010;
int vis[maxn];
struct Edge{
int to,nxt;
}edge[maxn];
int n,m,tot;
int head[maxn],color[maxn],linker[maxn];
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v){
edge[tot].to=v;
edge[tot].nxt=head[u];
head[u]=tot++;
}
int bfs(){
int s,now,cnt,v;
memset(color,-1,sizeof(color));
queue<int>q;
q.push(1);
color[1]=1;
while(!q.empty()){
s=q.front();
q.pop();
for(int i=head[s];i!=-1;i=edge[i].nxt){
v=edge[i].to;
if(color[v]==-1){
color[v]=1-color[s];
q.push(v);
}
else if(color[v]==color[s]){
return 1;
}
}
}
return 0;
}
int Find(int u){
for(int j=head[u];j!=-1;j=edge[j].nxt){
int v=edge[j].to;
if(!vis[v]){
vis[v]=1;
if(linker[v]==-1||Find(linker[v])){
linker[v]=u;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
memset(linker,-1,sizeof(linker));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
int u,v;
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
add(u,v);
}
if(bfs()){
printf("No\n");
continue;
}
printf("%d\n",solve());
}
return 0;
}
题目链接
题解:
课程看成二分图一部分、学生看成二分图另一部分。
然后直接用匈牙利算法求最大匹配即可,
如果最大匹配数=课程数,则:YES
否则:NO
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=310;
int mp[maxn][maxn];
int a[maxn],vis[maxn];
int n,m;
int Find(int i){
for(int j=1;j<=m;j++){
if(mp[i][j]&&vis[j]==0){
vis[j]=1;
if(a[j]==0||Find(a[j])==1){
a[j]=i;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)==1){
ans++;
}
}
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(mp,0,sizeof(mp));
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int num,j;
scanf("%d",&num);
while(num--){
scanf("%d",&j);
mp[i][j]=1;
}
}
int ans=solve();
if(ans==n){
printf("YES\n");
}
else{
printf("NO\n");
}
}
return 0;
}
题目链接
题意:
n*m的棋盘,其中只有k个位置可以放棋子。同一行或者同一列不能放“车”,题目其实有两问
1.问最多可以放多少个棋子
2.问在第1问的基础上,有多少个棋子的位置是不能改变的,如果改变第1问的结果就会改变
题解:
针对这两个问题,分别来处理:
1.直接用匈牙利算法求二分图的最大匹配即可
2.对每个已经匹配的边,先删除,然后判断最大匹配数是不是相等,如果不相等则这个位置是不能变的,结果+1;最后输入结果即可
开始一直样例都过不了,原来是在后面判断边的时候,对匹配数组没有初始化。。。。。。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=110;
int mp[maxn][maxn];
int match1[maxn],vis[maxn],match2[maxn];
int n,m,k;
int Find(int i){
for(int j=1;j<=m;j++){
if(vis[j]==0&&mp[i][j]){
vis[j]=1;
if(Find(match1[j])||match1[j]==0){
match1[j]=i;
return 1;
}
}
}
return 0;
}
int solve(){
memset(match1,0,sizeof(match1)); //每次初始化
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
int cas=1;
while(~scanf("%d%d%d",&n,&m,&k)){
memset(mp,0,sizeof(mp));
int u,v;
while(k--){
scanf("%d%d",&u,&v);
mp[u][v]=1;
}
int num=solve();
int ans=0;
memcpy(match2,match1,sizeof(match1));
for(int v=1;v<=n;v++){
if(match2[v]==0){
continue;
}
int u=match2[v];
mp[u][v]=0;
int x=solve();
if(x!=num){
ans++;
}
mp[u][v]=1;
}
printf("Board %d have %d important blanks for %d chessmen.\n",cas++,ans,num);
}
return 0;
}
题目链接
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=110;
int mp[maxn][maxn];
int vis[maxn],match[maxn];
struct node{
int c1,c2;
};
node a[maxn*maxn];
int n;
int Find(int i){
for(int j=1;j<=n;j++){
if(vis[j]==0&&mp[i][j]){
vis[j]=1;
if(match[j]==0||Find(match[j])){
match[j]=i;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
memset(match,0,sizeof(match));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
while(~scanf("%d",&n)){
memset(mp,0,sizeof(mp));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&mp[i][j]);
}
}
int num=solve();
if(num!=n){
printf("-1\n");
continue;
}
int t=0;
for(int c=1;c<=n;c++){
while(c!=match[c]){
a[++t].c1=c;
a[t].c2=match[c];
swap(match[c],match[match[c]]);
}
}
printf("%d\n",t);
for(int i=1;i<=t;i++){
printf("C %d %d\n",a[i].c1,a[i].c2);
}
}
return 0;
}
题目链接
题解:
这题如果继续用匈牙利算法是肯定会tle的
于是采用Hopcroft-Karp算法,而且还必须用邻接表来存二分图
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=3010;
struct node{
int x,y,v;
};
node a[maxn],b[maxn];
int vis[maxn],usedx[maxn],usedy[maxn];
int n,m,tot;
int dx[maxn],dy[maxn],depth,head[maxn];
struct Edge{
int v,nxt;
}edge[maxn*maxn];
void add(int u,int v){
edge[tot].v=v;
edge[tot].nxt=head[u];
head[u]=tot++;
}
int Find(int u){
for(int i=head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].v;
if(vis[v]==0&&dx[u]==dy[v]-1){
vis[v]=1;
if(!usedy[v]||Find(usedy[v])){
usedy[v]=u;
usedx[u]=v;
return 1;
}
}
}
return 0;
}
bool bfs(){
queue<int>q;
depth=inf;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
for(int i=1;i<=n;i++){
if(!usedx[i]){
dx[i]=0;
q.push(i);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
if(depth<dx[u]){
break;
}
for(int j=head[u];j!=-1;j=edge[j].nxt){
int v=edge[j].v;
if(dy[v]==-1){
dy[v]=dx[u]+1;
if(!usedy[v]){
depth=dy[v];
}
else{
dx[usedy[v]]=dy[v]+1;
q.push(usedy[v]);
}
}
}
}
if(depth==inf){
return 0;
}
return 1;
}
void init(){
memset(head,-1,sizeof(head));
tot=0;
}
int main(){
int cas;
int t,x,y;
scanf("%d",&cas);
for(int k=1;k<=cas;k++){
init();
scanf("%d%d",&t,&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
for(int j=1;j<=n;j++){
int d=(a[j].x-x)*(a[j].x-x)+(a[j].y-y)*(a[j].y-y);
if(d<=a[j].v*t*a[j].v*t){
add(j,i);
}
}
}
int ans=0;
memset(usedx,0,sizeof(usedx));
memset(usedy,0,sizeof(usedy));
while(bfs()){
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
if(!usedx[i]&&Find(i)){
ans++;
}
}
}
printf("Scenario #%d:\n",k);
printf("%d\n\n",ans);
}
return 0;
}
题目链接
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=610;
char mp[maxn][maxn];
int g[maxn][maxn];
int relate[maxn][maxn];
int match[maxn],vis[maxn];
int t,n;
int temp;
int Find(int u){
for(int v=0;v<temp;v++){
if(vis[v]==0&&g[u][v]){
vis[v]=1;
if(match[v]==0||Find(match[v])){
match[v]=u;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
memset(match,0,sizeof(match));
for(int i=0;i<temp;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
scanf("%d",&t);
int cas=1;
while(t--){
scanf("%d",&n);
getchar();
memset(relate,0,sizeof(relate));
memset(g,0,sizeof(g));
temp=0;
for(int i=0;i<n;i++){
scanf("%s",mp[i]);
getchar();
for(int j=0;j<n;j++){
if(mp[i][j]=='#'){
relate[i][j]=temp++;
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(mp[i][j]!='#')
continue;
if(mp[i-1][j]=='#'){
g[relate[i][j]][relate[i-1][j]]=1;
}
if(mp[i+1][j]=='#'){
g[relate[i][j]][relate[i+1][j]]=1;
}
if(mp[i][j-1]=='#'){
g[relate[i][j]][relate[i][j-1]]=1;
}
if(mp[i][j+1]=='#'){
g[relate[i][j]][relate[i][j+1]]=1;
}
}
}
int ans=solve();
printf("Case %d: %d\n",cas++,ans/2);
}
return 0;
}
题目链接
题解:
最少路径点覆盖=原图点个数-新图最大匹配数
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=50;
const int maxm=15;
const int maxnum=maxn*maxm;
int mp[maxnum][maxnum];
int vis[maxnum];
int match[maxnum],newG[maxn][maxm];
char G[maxn][maxm];
int n,m,cnt;
int Find(int i){
for(int j=1;j<=cnt;j++){
if(vis[j]==0&&mp[i][j]){
vis[j]=1;
if(match[j]==0||Find(match[j])){
match[j]=i;
return 1;
}
}
}
return 0;
}
int solve(){
int num=0;
memset(match,0,sizeof(match));
for(int i=1;i<=cnt;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
num++;
}
}
return num;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
cnt=0;
memset(mp,0,sizeof(mp));
memset(newG,0,sizeof(newG));
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%s",G[i]);
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(G[i][j]=='*'){
cnt++;
newG[i][j]=cnt;
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(newG[i][j]>0){
if(i!=0){
if(newG[i-1][j]>0){
mp[newG[i][j]][newG[i-1][j]]=1;
}
}
if(i!=n-1){
if(newG[i+1][j]>0)
mp[newG[i][j]][newG[i+1][j]]=1;
}
if(j!=0){
if(newG[i][j-1]>0){
mp[newG[i][j]][newG[i][j-1]]=1;
}
}
if(j!=m-1){
if(newG[i][j+1]>0)
mp[newG[i][j]][newG[i][j+1]]=1;
}
}
}
}
printf("%d\n",cnt-solve()/2);
}
return 0;
}
题目链接
题解:
最小点覆盖=最大匹配数
这题通过题目所给,发现用邻接表建二分图要方便很多,求最大匹配数直接用匈牙利算法即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1501;
const int mod=7777777;
struct Edge{
int to,nxt;
}edge[maxn*maxn];
int head[maxn],match[maxn];
bool vis[maxn];
int n,tot;
void add(int u,int v){
edge[tot].to=v;
edge[tot].nxt=head[u];
head[u]=tot++;
}
void init(){
tot=0;
memset(head,-1,sizeof(head));
memset(edge,0,sizeof(edge));
}
int Find(int u){
for(int i=head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if(vis[v]==0){
vis[v]=1;
if(match[v]==-1||Find(match[v])){
match[v]=u;
return 1;
}
}
}
return 0;
}
int solve(){
memset(match,-1,sizeof(match));
int ans=0;
for(int i=0;i<n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
int u,v,num;
while(~scanf("%d",&n)){
init();
for(int i=1;i<=n;i++){
scanf("%d:(%d)",&u,&num);
for(int j=1;j<=num;j++){
scanf("%d",&v);
add(u,v);
add(v,u); //无向图建双边
}
}
printf("%d\n",solve()/2);
}
return 0;
}
题目链接
题解:
看题就知道又是一道非常经典的二分图匹配问题,发现数据不大,匈牙利算法可以过,于是先把最大匹配数给求出来,与答案一比,发现:答案=顶点个数-最大匹配数
其实题目是求最小路径覆盖;
最小路径覆盖=顶点个数-最大匹配数
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=125;
const int mod=7777777;
int mp[maxn][maxn];
int match[maxn],vis[maxn];
int n,m,t;
int Find(int u){
for(int v=1;v<=n;v++){
if(vis[v]==0&&mp[u][v]){
vis[v]=1;
if(match[v]==0||Find(match[v])){
match[v]=u;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
scanf("%d",&t);
while(t--){
memset(mp,0,sizeof(mp));
memset(match,0,sizeof(match));
scanf("%d%d",&n,&m);
int u,v;
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
mp[u][v]=1;
}
int ans=solve();
printf("%d\n",n-ans);
}
return 0;
}
题目链接
题解:
开始没怎么看题目,以为就是简单的最小路径覆盖,但是一交wa了,后面也是参考的别人的题解,原来是点可以重复(好吧,原谅我英文不好),然后利用图论中的Floyd算法求传递闭包(只能说绝了,我是真的没想到,感觉离散也是白学了。。。)
然后用匈牙利算法求一下最小路径覆盖即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const double pi=3.14159;
const int maxn=3000;
const int mod=1e3;
int mp[510][510];
int vis[510],match[510];
int n,m;
int Find(int i){
for(int j=1;j<=n;j++){
if(vis[j]==0&&mp[i][j]){
vis[j]=1;
if(match[j]==0||Find(match[j])){
match[j]=i;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
memset(match,0,sizeof(match));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(n+m==0)
break;
int u,v;
memset(mp,0,sizeof(mp));
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
mp[u][v]=1;
}
//重点
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
mp[i][j]|=(mp[i][k]&mp[k][j]);
}
}
}
int ans=solve();
printf("%d\n",n-ans);
}
return 0;
}
题目链接
题解:
乍一看,觉得就是最大独立集,但是一直没有找到突破口。
我们可以存储每个孩子喜欢的动物和不喜欢的动物,只有A喜欢的动物恰好是B不喜欢的动物时,建边,这就是建立二分图的过程
但是需要注意的是:只有一群孩子,而我们要建立二分图,也就相当于每个孩子既在左边又在右边(即:匹配了两次,最终结果需要除2)
最后:最大独立集=点数-最大匹配数(用匈牙利算法即可)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const double pi=3.14159;
const int maxn=3000;
const int mod=1e3;
int mp[510][510];
int vis[510],match[510];
int n,m,p;
char like[510][10],dislike[510][10];
int Find(int i){
for(int j=1;j<=p;j++){
if(vis[j]==0&&mp[i][j]){
vis[j]=1;
if(match[j]==0||Find(match[j])){
match[j]=i;
return 1;
}
}
}
return 0;
}
int solve(){
int ans=0;
memset(match,0,sizeof(match));
for(int i=1;i<=p;i++){
memset(vis,0,sizeof(vis));
if(Find(i)){
ans++;
}
}
return ans;
}
int main(){
while(~scanf("%d%d%d",&n,&m,&p)){
memset(mp,0,sizeof(mp));
for(int i=1;i<=p;i++){
scanf("%s%s",like[i],dislike[i]);
}
//A同学喜欢的,恰好B同学不喜欢建无向图
for(int i=1;i<=p;i++){
for(int j=1;j<=p;j++){
if(strcmp(like[i],dislike[j])==0||strcmp(like[j],dislike[i])==0){
mp[i][j]=1;
}
}
}
printf("%d\n",p-solve()/2); //因为小孩只有一群,然而我们要假设为两群建立二分图,也就相当于匹配了两次
}
}
题目链接
题解:
带权的二分图最大匹配,KM算法
另写了一篇博客:
HDU 2255:奔小康赚大钱(带权二分图最大匹配-KM算法)