您的位置:首页 > 其它

南京理工校赛同步 I题 puzzle 2-sat+二分

2016-04-18 21:26 295 查看

puzzle

Time Limit: 1000MS

Memory Limit: 65536KB

[显示标签]

Description

小明在玩一个闯关游戏,共n关,必须按顺序通过,每一关会遇到两个数字(可能一样),他要选择其中一个,如果要选的这个数字是他之前选过的就直接过了这关。但是他如果在某一关选了数字X,那就不能在经过另一关时选择数字Y当X+Y=2*n-1时,如果在某一关没有数可选的时候游戏结束,问在闯关游戏中小明最多可以通过几关。

Input

第一行输入一个整数T(1 <= T <= 100),表示接下来T组测试数据,每组测试数据第一行一个整数n(900<=n<=1000)接下来n行每行两个整数a,b代表每关遇到的数字(0<=a,b<=2*n-1)

Output

每组测试数据输出一个整数,小明最多的过关数。

Sample Input

2
3
0 0
1 1
2 2
3
0 0
1 1
5 5


Sample Output

3
2
蒟蒻没听过2-sat dfs bfs xjb搜各种超时爆内存
今天学了下2-sat
这题主要技巧在 二分枚举可能通过的关
ACcode:
#include <map>
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define maxn 10000
using namespace std;
struct Node{int to,next;}edge[maxn];
int head[maxn],tot;
void add(int u,int v){edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;}
int low[maxn],dfn[maxn],Stack[maxn],Belong[maxn];
bool Instack[maxn];
int Index,scc,top;
void init(){tot=0;memset(head,-1,sizeof(head));}
void tarjan(int u){
int v;
low[u]=dfn[u]=++Index;
Stack[top++]=u;
Instack[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].to;
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(Instack[v]&&low[u]>dfn[v])low[u]=dfn[v];
}
if(low[u]==dfn[u]){
scc++;
do{
v=Stack[--top];
Instack[v]=false;
Belong[v]=scc;
}while(v!=u);
}
}
void solve(int n){
memset(dfn,0,sizeof(dfn));
memset(Instack,false,sizeof(Instack));
Index=scc=top=0;
for(int i=0;i<n;++i)if(!dfn[i])tarjan(i);
}
int x[maxn],y[maxn];
int main(){
int t,n,a,b,N;
scanf("%d",&t);
while(t--){
scanf("%d",&n);N=2*n-1;
for(int i=0;i<n;++i)
scanf("%d%d",&x[i],&y[i]);
int lf=0,rt=n;
while(lf<=rt){
init();
int mid=(lf+rt)>>1;
for(int i=0;i<mid;++i){
add(N-x[i],y[i]);
add(N-y[i],x[i]);
}
solve(2*n);
bool flag=true;
for(int i=0;i<n&&flag;++i)
if(Belong[i]==Belong[N-i])
flag=false;
if(flag)lf=mid+1;
else rt=mid-1;
}
printf("%d\n",(lf+rt)/2);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: