Happy-2018-Graph theory 题解

bzoj 4443

首先很容易看出这是一个需要使用二分的二分图,要选出第K大的数,即找到第n-k+1小的数,对于题目中的两个数字不能在同一行或同一列,这是一个在二分图题目中常见的条件,对应的解决方法就是使用行列建边
所以最后的过程是首先我们二分出一个值,对于小于等于这个值的数,进行行列建图,最后判断匹配数是否为n-k+1即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=65000;
const int maxm=maxn*2;
struct Node
{
	int to;
	int next;
}edge[maxm];
int cnt;
int n,m,k;
int head[maxn];
int map[310][310];
int match[maxn];
bool vis[maxn];
void init()
{
	cnt=0;
	memset(head,-1,sizeof(head));
	return;
}
void add(int u,int v)
{
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	return;
}
void build(int mid)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(map[i][j]<=mid)
			{
				add(i,j+n);
			}
		}
	}
	return;
}
bool dfs(int node)
{
	for(int i=head[node];~i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!vis[v])
		{
			vis[v]=true;
			if(match[v]==-1||dfs(match[v]))
			{
				match[v]=node;
				return true;
			}
		}
	}
	return false;
}
int hungry()
{
	int ans=0;
	memset(match,-1,sizeof(match));
	for(int i=1;i<=n;i++)
	{
		memset(vis,false,sizeof(vis));
		if(dfs(i))
		{
			ans++;
		}
	}
	return ans;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		int minn=0x3f3f3f3f;
		int maxx=-1;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				scanf("%d",&map[i][j]);
				minn=min(minn,map[i][j]);
				maxx=max(maxx,map[i][j]);
			}
		}
		int left=minn;
		int right=maxx;
		while(left<=right)
		{
			int mid=(left+right)>>1;
			init();
			build(mid);
			int res=hungry();
			if(res>=n-k+1)
			{
				right=mid-1;
			}
			else
			{
				left=mid+1;
			}
		}
		cout<<left<<endl;
	}
	return 0;
}

HDU 1596

dijkstra变形,很多人都写出来了,不多说,注意浮点数就好了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1010;
//const double inf=0x3f3f3f3f+0.5;
int n;
double map[maxn][maxn];
double dis[maxn];
bool book[maxn];
void dijkstra(int start,int end)
{
	memset(book,false,sizeof(book));
	for(int i=1;i<=n;i++) dis[i]=map[start][i];
	book[start]=true;
	dis[start]=1;
	for(int i=1;i<=n;i++)
	{
		double minn=0;
		int flag=start;
		for(int j=1;j<=n;j++)
		{
			if(!book[j]&&dis[j]>minn)
			{
				minn=dis[j];
				flag=j;
			}
		}
		if(flag==end) break;
		book[flag]=true;
		for(int j=1;j<=n;j++)
		{
			if(!book[j]&&dis[j]<dis[flag]*map[flag][j]){
				dis[j]=dis[flag]*map[flag][j];
			}
		}
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				scanf("%lf",&map[i][j]);
			}
		}
		int q;
		scanf("%d",&q);
		for(int i=0;i<q;i++)
		{
			int start,end;
			scanf("%d%d",&start,&end);
			dijkstra(start,end);
			if(dis[end]==0) printf("What a pity!\n");
			else printf("%.3f\n",dis[end]);
		}
	}
}

HDU 6290

博客链接:https://www.cnblogs.com/luoxiaoyi/p/9726829.html

HDU 6187

其实是一道很好想的题,如果国王要去别的地方,那么肯定不能出现环把它围住,又要求拆除边数总权值最少,求个最大生成树即可

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5+7;
const int maxm=2e5+7;
struct Node
{
	int u,v,w;
}edge[maxm];
int tree[maxn];
bool cmp(Node a,Node b)
{
	return a.w>b.w;
}
int find(int node)
{
	return tree[node]==node?node:tree[node]=find(tree[node]);
}
int main()
{
    //freopen("in.txt","r",stdin);
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		for(int i=1;i<=n;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			tree[i]=i;
		}
		int sum=0;
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
			sum+=edge[i].w;
		}
		sort(edge,edge+m,cmp);
		int cnt=0;
		int ans=0;
		for(int i=0;i<m;i++)
		{
			int x=find(edge[i].u);
			int y=find(edge[i].v);
			if(x!=y)
			{
				tree[x]=y;
				ans+=edge[i].w;
				cnt++;
			}
		}
		cout<<m-cnt<<' '<<sum-ans<<endl;
	}
	return 0;
}

HDU 2819

二分图匹配
根据线性代数的知识,对于一个单位矩阵,它的秩为n。任意交换矩阵的两行或两列,矩阵的秩是不会改变的。所以如果通过行变换无法使这个矩阵变为单位矩阵,那么通过列变换一定也不行。
那么我们通过行和列来构建这个二分图,对于任意的 1 ≤ i ≤ n 1\leq i \leq n 1in,若num[i][j]为1,那么就让i与j连一条边,就表示第j列可以移动到第i行的第i列

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const int maxm=1e5+7;
struct Node
{
	int to;
	int next;
}edge[maxm];
int cnt;
int head[maxn];
int num[110][110];
bool vis[maxn];
int match[maxn];
int n;
void init()
{
	cnt=0;
	memset(head,-1,sizeof(head));
	return;
}
void add(int u,int v)
{
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	return;
}
int dfs(int node)
{
	for(int i=head[node];~i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!vis[v])
		{
			vis[v]=true;
			if(match[v]==-1||dfs(match[v]))
			{
				match[v]=node;
				return true;
			}
		}
	}
	return false;
}
int hungry()
{
	int ans=0;
	memset(match,-1,sizeof(match));
	for(int i=1;i<=n;i++)
	{
		memset(vis,false,sizeof(vis));
		if(dfs(i))
		{
			ans++;
		}
	}
	return ans;
}
int main()
{
	//freopen("in.txt","r",stdin);
	while(~scanf("%d",&n))
	{
		init();
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				scanf("%d",&num[i][j]);
			}
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(num[i][j])
				{
					add(i,j);
				}
			}
		}
		int res=hungry();
		if(res<n)
		{
			puts("-1");
		}
		else
		{
			vector<pair<int,int> > vec;
			for(int i=1;i<=n;i++)
			{
				if(match[i]==i) continue;
				//if(match[i]==-1) continue;
				for(int j=1;j<=n;j++)
				{
					if(i==j) continue;
					if(match[j]==i)
					{
						vec.push_back(make_pair(i,j));
						swap(match[i],match[j]);
						//match[j]=match[i]=-1;
					}
				}
			}
			int len=vec.size();
			cout<<len<<endl;
			for(int i=0;i<len;i++)
			{
				cout<<"C "<<vec[i].first<<' '<<vec[i].second<<endl;
			}
		}
	}
	return 0;
}
/*
3
0 0 1 
1 0 0
0 1 0
*/

HDU 1102

最小生成树的模板题,不多说

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=110;
const int inf=0x3f3f3f3f;
int map[maxn][maxn];
int dis[maxn];
bool book[maxn];
int n;
int prim()
{
	memset(book,false,sizeof(book));
	for(int i=1;i<=n;i++) dis[i]=map[1][i];
	for(int i=0;i<n;i++)
    {
		int minn=inf;
		int flag=1;
		for(int j=1;j<=n;j++)
        {
			if(!book[j]&&dis[j]<minn){
				minn=dis[j];
				flag=j;
			}
		}
		book[flag]=true;
		for(int j=1;j<=n;j++)
        {
			if(!book[j]&&dis[j]>map[flag][j])
            {
				dis[j]=map[flag][j];
			} 
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
    {
		ans+=dis[i];
	}
	return ans;
}
int main()
{
	while(cin>>n)
    {
		for(int i=1;i<=n;i++)
        {
			for(int j=1;j<=n;j++)
            {
				cin>>map[i][j];
			}
		}
		int m;
		cin>>m;
		while(m--)
        {
			int a,b;
			cin>>a>>b;
			map[a][b]=map[b][a]=0;
		}
		cout<<prim()<<endl;
	}
}

bzoj 1050

https://blog.csdn.net/clove_unique/article/details/52612011

CodeForces 989C

https://blog.csdn.net/I_believe_CWJ/article/details/80660869

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值