日常鸣谢kuangbin博主
http://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html
这是原文地址;
下面是我自己的理解:
hdu 1814
n个党派,每个党派有两个人 2*i-1、2i
12 34 56 78 ….
现要组建委员会,没个党派都需要派一个人
m对关系
a b 表示a和b相互讨厌
求一种字典序最小的组建方法,没有输出 NIE
个人觉得: 看代码就好了,代码十分之详细,这个确实可作为模板,写的很好
struct node{
int to,next;
}edge[100010];
int head[100010],tot;
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
bool vis[20005]; // 染色法
int sta[20010],top;
bool dfs(int u ){ //染色
if(vis[u^1]) // 0^1=1 1^1=0 2^1=3 3^1=2 4^1=5 5^1=4
return false;
if(vis[u])
return true;
vis[u]=1;
sta[top++]=u;
for(int i=head[u];i!=-1;i=edge[i].next){ //标记u,并且去寻找与u相关的点
if(!dfs(edge[i].to))
return false;
}
return true;
}
bool twosat(int n){
memset(vis,0,sizeof(vis));
for(int i=0;i<2*n;i+=2){ // 0. 2. 3. 4. 5. 6.
if(vis[i] || vis[i^1]) continue; //每一个党派只能选一个人
top=0;
if(!dfs(i)){ //对i 寻找路径找 不到合法的 解法。
while(top)
vis[sta[--top]]=false;
if( !dfs(i^1) ) // 对i和i^1都没有合法路径,那么NIE
return false;
}
}
return true;
}
int main()
{
// freopen("1.txt","r",stdin);
int n,m;
int u,v;
while(~scanf("%d %d",&n,&m)){
init();
while(m--){
scanf("%d %d",&u,&v);
u--,v--;
addedge(u,v^1); //原关系:u,v 加边:u-v^1 v-u^1
addedge(v,u^1); //所以 我们的边 :表示选ab 是同时选。 即u and v
// printf("add:%d %d %d %d\n",u,v^1,v,u^1);
}
if(twosat(n)){
for(int i=0;i<2*n;i++)
if(vis[i])
printf("%d\n",i+1);
}
else
printf("NIE\n");
}
return 0;
}
hdu 3622
n对炸弹, 每次安置炸弹的时候只能选这一对中的一个,每个炸弹爆炸范围r都是一样,
求一个最大的r, 使所有爆炸范围互不相交
二分结果 判断可行性
n=100
可行性判断:
边的意义: 选a 则 选b
不知道kuangbin 大大博客上的解法为何要写那么复杂,还用了强连通分量,又是原图又是逆图,感觉稍显麻烦
我们可以和上一题差不多的做法,给 可行边建图,然后我们用dfs判断可行性。
#define eps 1e-7
struct node{
int to,next;
}edge[100010];
int head[100010],tot;
bool vis[20005]; // 染色法
void init(){
tot=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
}
void addedge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
int sta[20010],top;
bool dfs(int u ){ //染色
if(vis[u^1]) // 0^1=1 1^1=0 2^1=3 3^1=2 4^1=5 5^1=4
return false;
if(vis[u])
return true;
vis[u]=1;
sta[top++]=u;
for(int i=head[u];i!=-1;i=edge[i].next){ //标记u,并且去寻找与u相关的点
if(!dfs(edge[i].to))
return false;
}
return true;
}
bool twosat(int n){
for(int i=0;i<2*n;i+=2){ // 0. 2. 3. 4. 5. 6.
if(vis[i] || vis[i^1]) continue; //每一对 点只能选一个炸弹
top=0;
if(!dfs(i)){ //对i 寻找路径找 不到合法的 解法。
while(top)
vis[sta[--top]]=false;
if( !dfs(i^1) ) // 对i和i^1都没有合法路径,那么NIE
return false;
}
}
return true;
}
double x[105*2],y[105*2];
/*
01 23 45 67 89
*/
double dist(double x,double y,double a,double b){
return sqrt( (x-a)*(x-a) + (y-b)*(y-b) );
}
int main()
{
freopen("1.txt","r",stdin);
int n,m;
while (~scanf("%d", &n)) {
for (int i = 0; i < n; i++) {
scanf("%lf %lf %lf %lf ", &x[2 * i], &y[2 * i], &x[2 * i + 1],
&y[2 * i + 1]);
}
double l = 0, r = 1000000;
for (int i = 1; i <= 100; i++) {
double mid = (l + r) / 2;
init();
for (int i = 0; i < 2 * n - 2; i++) { // 01.23..2n-2 2n-1
int sta;
if (i & 1)
sta = i + 1;
else
sta = i + 2;
for (int j = sta; j < 2 * n; j++) {
double dis = dist(x[i], y[i], x[j], y[j]);
if (dis - 2 * mid < eps) {
addedge(i, j ^ 1);
addedge(j, i ^ 1); // u-v 是可行边
// printf("add : %d %d ,%d %d\n",i,j^1,j,i^1);
}
}
}
if (twosat(n))
l = mid + 0.00001;
else
r = mid - 0.00001;
}
printf("%.2f\n", l);
}
return 0;
}