E. Graph Coloring(二分图+背包dp)

news/2024/7/7 14:56:20

题目

题意:

    给定一张图,要求给每个点染色,颜色为1,2,3。要求1的个数为n1,2的个数为n2,3的个数为n3,并且对于任意两个相邻的点颜色的差的绝对值为1。
     1 ≤ n ≤ 5000 , 0 ≤ m ≤ 1 0 5 , 0 ≤ n 1 , n 2 , n 3 ≤ n 1≤n≤5000,0≤m≤10^5,0≤n1,n2,n3≤n 1n5000,0m105,0n1,n2,n3n

分析:

    分析一下其实可以发现,染1和染3其实是等价的,因为他们只能与2相邻。所以我们就可以把染2的点和染别的颜色的点分为两个集合。对于这两个集合其实就是一张二分图。
    但是由于图并不连通,所以我们会有很多的二分图。对于这么多的二分图,我们需要从每个二分图里找集合,合成n2。显然就是使用背包dp一下即可。最后输出方案,把1输出完了之后输出3即可。

#include <iostream>
#include <vector> 
#include <cstring>
using namespace std;

vector<int> g[5005];
int vis[5005],num[10005],used[10005];
int dp[10005][5005];
int cnt = 0;
int flag = 0;

void dfs(int x,int id)
{
	vis[x] = id;
	num[id] ++;
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i];
		if( vis[t] == -1 ) dfs(t,id^1);
		else
		{
			if( (vis[t] ^ 1) != vis[x] )
				flag = 1;
		}
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n,m,n1,n2,n3;
	cin >> n >> m;
	cin >> n1 >> n2 >> n3;
	for (int i = 1; i <= m; i++)
	{
		int x,y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	memset(vis,-1,sizeof(vis));
	for (int i = 1; i <= n; i++)
	{
		if( vis[i] == -1 ) 
		{
			dfs(i,cnt);
			cnt += 2;
		}
		if( flag )
		{
			cout << "NO" << '\n';
			return 0;
		}
	}
	for (int i = 0; i < cnt; i += 2)
	{
		for (int j = 0; j <= n2; j++)
		{
			if( i == 0 )
			{
				if( j == num[i] || j == num[i+1] ) dp[i][j] = 1;
			}else
			{
				if( j >= num[i] ) dp[i][j] = max(dp[i][j],dp[i-2][j-num[i]]);
				if( j >= num[i+1] ) dp[i][j] = max(dp[i][j],dp[i-2][j-num[i+1]]);
			}
		}
	}
	if( dp[cnt-2][n2] == 0 ) 
	{
		cout << "NO" << '\n';
		return 0;
	}
	else
	{
		int i = cnt - 2,j = n2;
		while( i >= 0 )
		{
			if( i == 0 )
			{
				if( j == num[i] ) used[i] = 1;
				else used[i+1] = 1;
			}else
			{
				if( dp[i-2][j-num[i]] )
				{
					used[i] = 1;
					j -= num[i];
				}else
				{
					used[i+1] = 1;
					j -= num[i+1];
				}
			}
			i -= 2;
		}
	}
	cout << "YES" << '\n';
	for (int i = 1; i <= n; i++)
	{
		if( used[vis[i]] == 1 ) cout << 2;
		else
		{
			if( n1 )
			{
				cout << 1;
				n1 --;
			}else cout << 3; 
		}
	}
	cout << endl;
	return 0;
}


http://www.niftyadmin.cn/n/1037137.html

相关文章

6.1.6 Gallery结合案例详解

Gallery&#xff08;相册&#xff09;控件是个很不错的图片查看控件&#xff0c;屏幕中有一个图片列表&#xff0c;Gallery类的继承关系如下&#xff1a; java.lang.Object ↳ android.view.View ↳ android.view.ViewGroup ↳ android.widget.AdapterView<T extends androi…

java 方法的基本概念及其方法的基本定义和使用

首先 我们需要了解方法是干什么的 比如你早上起床 刷牙 洗脸 这种每天都要做的事程序里也会有 比如 我们需要修改一个用户信息 首先我们要查询这个用户的信息 那么我们就可以把这个查询用户信息的过程 封装成一个方法 发放就是一块独立的代码块进行封装 然后要用到这段代码时直…

F. Kate and imperfection(思维)

题目 题意&#xff1a; 给定一个1…n的集合&#xff0c;要求分别求出长为2,3,4…n的子集&#xff0c;使得每个子集中任意两个数的gcd的最大值尽可能的小。     2≤n≤5⋅1052≤n≤5⋅10^52≤n≤5⋅105 分析&#xff1a; 这题就是一道思维题&#xff0c;经过灵光一闪后发现…

6.4 Android国际化和本地化

何谓国际化和本地化呢&#xff1f;就是在资源文件夹res内建立不同国家语言的文件&#xff0c;这些国家语言的文件命名是有规定的&#xff0c;具体参见表6-1。当用户设置手机的语言时&#xff0c;程序能根据用户选择的语言情况&#xff0c;而加载相对应的语言文件。用户感受到是…

7.2 LinearLayout布局详解

LinearLayout线性布局&#xff0c;线性布局是所有布局中最常用的&#xff0c;它可以让其中的子元素垂直或水平的方式排列&#xff08;通过排列方向的设置&#xff09;。通常复杂的布局都是在LinearLayout布局中嵌套而成的。 下面看一个LinearLayout的例子&#xff0c;这个例子中…

java 方法传递参数,定义返回值

方法可以从调用他的位置传递参数 比如 我们要做两位数的相加 我们可以把相加的两位数当成参数 从外面传给方法 在方法里写将两个数相加的操作 参考代码如下 public class index {public static void main(String args[]) {int naint 1;mint(naint, 100);}public static void …

E. Physical Education Lessons(set区间操作)

题目1 题意&#xff1a; 给定一个n&#xff0c;表示一共有n天的工作日。现在有两个操作&#xff0c;操作1将区间[l,r]全部变为工作日&#xff0c;操作2将区间[l,r]全部变为非工作日。一共有q次操作&#xff0c;要求输出每次操作后的工作日天数。     1≤n≤109,1≤q≤3⋅1…

7.3 RelativeLayout布局详解

RelativeLayout相对布局&#xff0c; 允许子元素指定他们相对于其它元素或父元素的位置&#xff08;通过ID 指定&#xff09;。因此&#xff0c;可以以左右对齐、上下对齐、置于屏幕中央等形式来排列元素。相对布局在实际应用中比较常用。图7-13所示是垂直方向上的应用。图7-13…