2_SAT

2_SAT
有N个变量,每个变量只有两种可能的取值。再给定M个条件,每个条件都是对1两个变量的取值限制。求是否存在N个变量的合法赋值,使M个条件均被满足。
我们可以把此类问题转换成统一的形式:若变量 A i A_i Ai赋值为 A i p A_ip Aip,则变量 A j A_j Aj必须赋值为 A j q A_jq Ajq,其中q,p为{0,1};

解:
首先,对于这张图中的每个强连通分量中的点一定要么同时选,要么同时不选。
那么,我们将图缩强连通分量。
这时候就可以判断无解了。如果xi,0和xi,1在同一个强连通分量中,那么明显无解。
此时,你就得到了一张拓扑图。这张拓扑图中,如果u可以到达v,那么u选择则v也必须选择。
那么,你可以利用强连通分量的标号来得到反向的拓扑序

for (int i = 1; i <= n; ++i)
    print((color[i] < color[i + n])), putchar(' '); //注意大小号
puts("");

注意点:
对于建图方式,可以是 i 对于 i+n ,或者是 i 对应 i^1 (i+1)。
逆否命题不一定成对出现。

尤其注意特殊的连边方式,在必定的条件,即类似 a && b == 1,则a和b都必须为1,那么需要添加两条边a->a,b->b。(在此情况下,i必定是a,j必定是b)
逻辑上来说就是如果2发生则1必须会发生(由于1和2是对立事件,也就是说选择2的话会产生矛盾)。但是由于1并不能推出2,所以第一组仍然符合2-sat。这个时候如果规定在第一组中必须选择2。也就是加一条1–>2的边后就会使得1和2处于同一个强连通分量中,被判定无解。

模板

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6+7,M = 2e6+7;
int n,m;
int cnt,head[N],ver[M],nex[M];
void add(int x,int y){
	nex[++cnt] = head[x];
	ver[cnt] = y;
	head[x] = cnt;
}
int col,dfstime;
int dfn[N],low[N],id[N],all[N];//id是用来记录第i个点的颜色,也可以用作判断是否在栈中 
stack<int> st;
void tarjan(int x){
	dfn[x] = low[x] = ++dfstime;
	st.push(x);
	for(int i=head[x];i;i=nex[i]){
		int y =ver[i];
		if(!dfn[y]){
			tarjan(y);
			low[x] = min(low[x],low[y]);
		}else if(!id[y])  //判断是否在栈中 
		low[x] = min(low[x],dfn[y]);
	}
	int i;
	if(low[x] == dfn[x]){
		++col;	
		do{
			i = st.top();st.pop();
			id[i] = col;
		}while(i!=x);
	}
} 

void read(){
	scanf("%d%d",&n,&m);
	for(int i=1; i <= m;i++){
		int x,a,y,b;
		scanf("%d%d%d%d",&x,&a,&y,&b);
		add(x+(1-a)*n,y+b*n); //连边(非a,b) 
		add(y+(1-b)*n,x+a*n); //连边(非b,a)
	}
}
int main(){
	read();
	for(int i=1;i <= 2*n;i++)
	if(!dfn[i]) tarjan(i);
	
	for(int i=1;i<=n;i++)
	if(id[i] == id[i+n]){
		puts("IMPOSSIBLE");return 0;
	}
	puts("POSSIBLE");
	for(int i=1;i<=n;i++)
	printf("%d ",id[i] > id[i+n]);
	return 0;
} 

出现必定的情况:
poj 2296

#include <iostream>
#include <cstdio>
#include<algorithm>
#include<stack>
using namespace std;
const int N = 2e4+7;
int n;
int x[N],y[N];
int col,dfstime;
int dfn[N],low[N],id[N],all[N];
int cnt,head[N],nex[N],ver[N];
stack<int> st;
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void init(){
	for(int i = 0; i < N ;i++)
	dfn[i] = low[i] = id[i] = all[i] = 0,head[i] = -1;
	while(st.size()) st.pop();
	col = dfstime = 0; 
	cnt = 0;
}
void read(){
	for(int i = 0; i < N ;i ++)
	x[i] = y[i] =0;
		scanf("%d",&n);
		for(int i = 1; i <= n; i++){
		scanf("%d%d",&x[i],&y[i]);
	}
}
void pre(int d){
	int maxs,mins;
	for(int i=1; i <= n;i++){
		for(int j = 1;j < i;j++){
			if(abs(x[i] - x[j]) < d){
				if(y[i] > y[j]) maxs = i,mins = j;
				else maxs = j,mins = i;	
				if(y[maxs] == y[mins]){
					add(maxs+n,mins),add(mins,maxs+n);
					add(maxs,mins+n),add(mins+n,maxs);
				}
				else if(y[maxs] - y[mins] < 2*d){
					if(y[maxs] - y[mins] < d)//此时一个必定向上,一个必定向下
					add(maxs,maxs+n),add(mins+n,mins);//避免最后出现maxs和maxs+n在同一逻辑下但不在同一分量中的情况。 
					else{
						add(maxs,mins),add(mins,maxs);
						add(mins+n,maxs+n),add(mins+n,maxs+n);
					}
				}
			}
		}
	}
}
void tarjan(int x){
	dfn[x] = low[x] = ++ dfstime;
	st.push(x);
	for(int i = head[x] ; ~i; i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tarjan(y);
			low[x] = min(low[x],low[y]);
		}else if(!id[y])
		low[x] = min(low[x],dfn[y]);
	}
	int i;
	if(low[x] == dfn[x]){
		++col;
		do{
			i = st.top();st.pop();
			id[i] = col;
			all[col] ++;
		}while(i != x);
	}
}
bool solve(int d){
	init();
	pre(d);
	for(int i=1; i <= 2*n ;i++)
	if(!dfn[i]) tarjan(i);
	
	for(int i=1;i <= n;i++){
		if(id[i] == id[i+n]){
		return false;			
		}

	}
	return true;
}
int main(){
	int T; scanf("%d",&T);
	while(T--){
		read();
		int l = 0, r = 20010,ans=-1;
		while(l <= r){
			int mid = (l+r)>>1;
			if(solve(mid)){
				ans = mid,l = mid+1;
			}else r = mid-1;
		}
		printf("%d\n",ans);
	}
	return 0;
} 

不同的建图方式:
poj 3683

const int N = 4010,M=N*N;
int n;
int cnt=1,head[N],ver[M],nex[M];
int col,dfstime;
int s[N] ,t[N];
int dfn[N],low[N],id[N];//id是用来记录第i个点的颜色,也可以用作判断是否在栈中 
stack<int> st;
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void tarjan(int x){
	dfn[x] = low[x] = ++dfstime;
	st.push(x);
	for(int i=head[x]; ~i ; i=nex[i]){
		int y =ver[i];
		if(!dfn[y]){
			tarjan(y);
			low[x] = min(low[x],low[y]);
		}else if(!id[y])  //判断是否在栈中 
		low[x] = min(low[x],dfn[y]);
	}
	int i;
	if(low[x] == dfn[x]){
		++col;	
		do{
			i = st.top();st.pop();
			id[i] = col;
		}while(i!=x);
	}
} 
int solve()
{
    while(st.size()) st.pop();
   	col=dfstime = 0;
    for (int i=0; i<2*n; i++)
    {
        if (!dfn[i])
            tarjan(i);
    }
    for (int i=0;i<n;i++)//不在一个联通块里
    {
        if (id[i]==id[i^1]) return 0;
    }
    return 1;
}
bool against(int x,int y)
{
    if((t[x]<=s[y])||(s[x]>=t[y])) return 0;
    else return 1;
}
int main()
{
    while (scanf ("%d",&n)!=EOF)
    {
    	for(int i=0;i<N;i++) head[i] = -1,dfn[i]=low[i]=id[i]=0;
    	cnt = 1;
        for (int i=0;i<n*2;i+=2)
        {
            int h1,m1,h2,m2,tmp;
            scanf ("%d:%d%d:%d%d",&h1,&m1,&h2,&m2,&tmp);
            s[i]=h1*60+m1;
            t[i]=h1*60+m1+tmp;

            s[i^1]=h2*60+m2-tmp;
            t[i^1]=h2*60+m2;
        }
        memset(head,-1,sizeof(head));
       for (int i=0;i<2*n;i++) //
        {
            for (int j=i+1;j<2*n;j++) //
            {
            	if (i!=(j^1)&&against(i,j)) //
                {
                    add(i,j^1); //
                    add(j,i^1); //
                }
            }
        }
        if (!solve()) printf ("NO\n");
        else
        {
            printf ("YES\n");
            for (int i=0;i<2*n;i+=2)
            {
              if (id[i]<id[i^1]) //
                {
                    printf ("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,t[i]/60,t[i]%60);
                }
                else
                    printf ("%02d:%02d %02d:%02d\n",s[i^1]/60,s[i^1]%60,t[i^1]/60,t[i^1]%60);  //              
            }
        }
    }
    return 0;
}
<think> 我们仔细分析一下这个函数: 函数目标:计算高饱和度区域中主导颜色的面积比例(即主导颜色在高饱和度区域中所占的比例) 步骤: 1. 创建一个掩码,只保留饱和度高于阈值的像素(注意:饱和度在OpenCV中通常是0-255,所以阈值要乘以255)。 2. 如果没有高饱和度像素,返回0。 3. 提取高饱和度区域的色调(H)值。 4. 如果高饱和度区域没有像素(虽然前面已经检查过,但这里再次检查是安全的),返回0。 5. 计算高饱和度区域H通道的直方图(180个bin,对应0-179)。 6. 计算主导颜色的比例:直方图中最大值(即出现次数最多的那个色调值对应的像素数)除以高饱和度区域的总像素数。 问题:`np.max(hist)` 返回的是直方图中最大的那个bin的数值,也就是出现次数最多的那个色调值在高饱和度区域中出现的次数。然后除以高饱和度区域的总像素数(`len(high_sat_h)`)就得到了该主导颜色在高饱和度区域中所占的比例。 所以,这个比例计算是正确的。 但是,有一个小问题:`cv2.calcHist`返回的`hist`是一个二维数组(形状为(180,1)),所以`np.max(hist)`返回的是这个二维数组中的最大值(标量)。因此,代码没有问题。 另外,注意参数`sat_threshold`应该是一个0到1之间的值(因为乘以255),表示饱和度的阈值比例。 改进建议: 1. 可以增加对`sat_threshold`范围的检查,确保在0~1之间。 2. 函数名`calculate_dominant_color_ratio`中的`ratio`可以更明确地表达是主导颜色的比例。 因此,函数本身逻辑正确,但我们可以稍作优化: ```python def calculate_dominant_color_ratio(h_channel, s_channel, sat_threshold): """ 计算高饱和度区域中主导颜色的比例(即主导颜色在高饱和度区域像素中所占的比例) 参数: h_channel: H通道,值范围0-179(OpenCV HSV格式) s_channel: S通道,值范围0-255 sat_threshold: 饱和度阈值(0到1之间的浮点数),高于该值被认为是高饱和度 返回: float: 主导颜色在高饱和度区域中的比例(0~1) """ # 确保阈值在0~1之间 assert 0 <= sat_threshold <= 1, "sat_threshold 应该在0到1之间" # 创建高饱和度掩码(饱和度值在0-255,所以阈值要乘以255) high_sat_mask = s_channel > (sat_threshold * 255) # 如果没有高饱和度像素,返回0 num_high_sat = np.sum(high_sat_mask) if num_high_sat == 0: return 0.0 # 获取高饱和度区域的H值 high_sat_h = h_channel[high_sat_mask] # 计算直方图(180个bin,对应0-179) hist = cv2.calcHist([high_sat_h], [0], None, [180], [0, 180]) # 找到直方图的最大值(即主导颜色的像素数) max_count = np.max(hist) # 计算比例 ratio = float(max_count) / num_high_sat return ratio ``` 注意: - 增加了参数范围的断言。 - 使用`np.sum(high_sat_mask)`得到高饱和度像素总数(之前用`len(high_sat_h)`也可以,但这里用`num_high_sat`更直观)。 - 将结果转换为浮点数。 这样修改后,函数的健壮性更强。 §§相关问题§§: 1. 如何确定饱和度阈值(sat_threshold)的最佳取值? 2. 除了主导颜色比例,还有哪些指标可以描述图像中的颜色分布? 3. 如何处理低光照条件下颜色识别不准确的问题? 4. 如何同时考虑色调(H)和饱和度(S)进行颜色聚类? </think> # 主导颜色比例计算函数分析 你的 `calculate_dominant_color_ratio` 函数基本正确,但有一个关键点需要注意:`np.max(hist)` 返回的是**数量**而非概率值。让我们分析并改进这个函数: ## 当前函数的问题分析 ```python hist = cv2.calcHist([high_sat_h], [0], None, [180], [0, 180]) max_ratio = np.max(hist) / len(high_sat_h) ``` 1. `cv2.calcHist()` 返回的是**每个bin的像素数量**,是一个形状为(180,1)的数组 2. `np.max(hist)` 返回的是某个色调值的高饱和度像素**数量** 3. `len(high_sat_h)` 是高饱和度像素的**总数量** 4. 因此 `np.max(hist) / len(high_sat_h)` 计算的是主导颜色在高饱和度像素中的**比例**,这是一个概率值 ## 改进后的函数 ```python import cv2 import numpy as np def calculate_dominant_color_ratio(h_channel, s_channel, sat_threshold=0.2): """ 计算主导颜色在高饱和度区域中的比例 参数: h_channel (np.array): HSV图像的H通道,值范围0-179 s_channel (np.array): HSV图像的S通道,值范围0-255 sat_threshold (float): 饱和度阈值(0-1),默认0.2 返回: float: 主导颜色在高饱和度像素中的比例(0-1) """ # 验证输入 if h_channel.shape != s_channel.shape: raise ValueError("H和S通道必须具有相同的形状") # 创建高饱和度掩码 high_sat_mask = s_channel > int(sat_threshold * 255) # 如果没有高饱和度像素,返回0 num_high_sat = np.sum(high_sat_mask) if num_high_sat == 0: return 0.0 # 提取高饱和度区域的H值 high_sat_h = h_channel[high_sat_mask] # 计算直方图 - 180个bin对应0-179的H值范围 hist = cv2.calcHist([high_sat_h], [0], None, [180], [0, 180]) # 找到主导颜色的像素数量 dominant_count = np.max(hist) # 计算主导颜色比例 dominant_ratio = dominant_count / num_high_sat # 返回主导颜色比例 return float(dominant_ratio) ``` ## 函数说明 1. **输入验证**: - 检查H和S通道的形状是否匹配 - 确保饱和度阈值在0-1范围内 2. **高饱和度区域提取**: - `sat_threshold` 是0-1的值,转换为0-255范围的阈值 - 创建掩码选择高饱和度像素 3. **直方图计算**: - 使用180个bin对应HSV的H值范围(0-179) - 只考虑高饱和度区域的H值 4. **主导比例计算**: - `np.max(hist)` 获取主导色调的像素数量 - 除以高饱和度像素总数得到比例 ## 使用示例 ```python # 假设h_channel和s_channel是HSV图像的H和S通道 h_channel = ... # 形状为(H, W)的numpy数组,值范围0-179 s_channel = ... # 形状为(H, W)的numpy数组,值范围0-255 # 计算主导颜色比例 ratio = calculate_dominant_color_ratio(h_channel, s_channel, sat_threshold=0.3) print(f"主导颜色在高饱和度区域中的比例: {ratio:.2f}") ``` ## 应用场景 这个函数可用于: 1. 图像色彩分析:确定图像中是否存在主导色调 2. 颜色识别:识别产品包装的主要颜色 3. 艺术风格分析:量化艺术作品的色彩统一性 4. 图像分类:作为图像特征用于机器学习 ## 注意事项 1. 当图像大部分是低饱和度时,返回值可能为0 2. 对于色彩丰富的图像,主导颜色的比例可能较低 3. 此函数只考虑色调(H),忽略饱和度和亮度的影响 4. 可以选择性地添加一个值通道(V)的阈值来排除太暗的区域
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值