残暴的逊哥又来血腥镇压我们了,然后给我们布置了很多图论题
有不少也是我没做过的,而且还学了不少新东西,撸完要好好记录一发
uva 11324
题意:这题貌似是给你一个有向图,然后定义一个东西叫clique,就是里面任意两点之间都有一条路,从u->v或者是v->u。问你最大的clique里有多少个点
题解:这题就是弱连通啊,先用tarjan缩点,然后从入读0的SCC开始dfs一条路径下去,看clique里最多有多少个点
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1005
#define MAXN 50005
#define maxnode 1005
#define sigma_size 4
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
const LL mod = (1<<64);
/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}
while(isdigit(tmp=getchar()));
return ret;
}
/*******************************************/
struct Edge{
int v,next;
}edge[MAXN];
int head[MAX];
int dfn[MAX];
int low[MAX];
int instack[MAX];
int sstack[MAX];
int belong[MAX];
int in[MAX];
int vis[MAX];
int num[MAX];
int tot,Index,top,cnt,maxn;
vector<int> v[MAX];
void init(){
mem(head,-1);
mem(dfn,0);
mem(low,0);
mem(instack,0);
mem(belong,0);
mem(num,0);
mem(in,0);
tot=0;
Index=0;
top=0;
cnt=0;
maxn=0;
}
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
void tarjan(int u){
dfn[u]=low[u]=++Index;
instack[u]=1;
sstack[++top]=u;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v]) low[u]=min(dfn[v],low[u]);
}
if(low[u]==dfn[u]){
cnt++;
while(1){
int k=sstack[top--];
belong[k]=cnt;
instack[k]=0;
num[cnt]++;
if(k==u) break;
}
}
}
void dfs(int u,int tmp){
if(maxn<tmp) maxn=tmp;
int n=v[u].size();
if(n==0) return ;
for(int i=0;i<n;i++){
int k=v[u][i];
if(!vis[k]){
vis[k]=1;
dfs(k,tmp+num[k]);
vis[k]=0;
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
add_edge(a,b);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=cnt;i++) v[i].clear();
for(int i=1;i<=n;i++){
for(int j=head[i];j!=-1;j=edge[j].next){
int vv=edge[j].v;
if(belong[i]!=belong[vv]){
in[belong[vv]]++;
v[belong[i]].push_back(belong[vv]);
}
}
}
for(int i=1;i<=cnt;i++){
if(!in[i]){
mem(vis,0);
vis[i]=1;
dfs(i,num[i]);
}
}
printf("%d\n",maxn);
}
return 0;
}
uva 11396
题意:这题貌似是给你一个图,每个点有3个度,然后问你能不能把这张图看成若干个爪子形状
题解:这题我一直没怎么读明白,看了题解很多题解也都没说清楚,就说个二分图
首先这题,可以把爪子中心的点看成一种关键点,爪子边上的点看成非关键点,然后每个爪子都有三个度,所以就是关键点绝对连着三个非关键点,非关键点必须连着三个关键点,不然没法把这图分成若干个爪子。(其实我还是不太明白题目到底说的怎么分割)
按照这个意思,就是判定二分图啊,就是染色问题
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1005
#define MAXN 50005
#define maxnode 1005
#define sigma_size 4
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
const LL mod = (1<<64);
/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}
while(isdigit(tmp=getchar()));
return ret;
}
/*******************************************/
struct Edge{
int v,next;
}edge[MAXN];
int head[MAX];
int col[MAX];
int tot;
void init(){
mem(head,-1);
mem(col,0);
tot=0;
}
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
bool dfs(int u){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!col[v]){
col[v]=-col[u];
if(!dfs(v)) return false;
}
else if(col[v]==col[u]) return false;
}
return true;
}
int main(){
int n;
while(scanf("%d",&n)&&n){
int a,b;
init();
while(scanf("%d%d",&a,&b)&&a){
add_edge(a,b);
add_edge(b,a);
}
int flag=0;
for(int i=1;i<=n;i++){
if(!col[i]){
col[i]=1;
if(!dfs(i)){
flag=1;
break;
}
}
}
if(flag) printf("NO\n");
else printf("YES\n");
}
return 0;
}
uva 10765
题意:又是一个看不懂题目意思的题,这个题就是给你一个无向连通图,然后问你去掉一个点,图变成多少个连通分量,问你前m中去点方法
题解:建图,tarjan求割点,求每个点下面连着几个儿子。也是比较明显的
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 10005
#define MAXN 1000005
#define maxnode 1005
#define sigma_size 4
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
const LL mod = (1<<64);
/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}
while(isdigit(tmp=getchar()));
return ret;
}
/*******************************************/
struct Edge{
int v,next;
}edge[MAXN];
struct Node{
int id,num;
bool operator < (const Node &a)const{
if(num==a.num) return id<a.id;
return num>a.num;
}
}node[MAX];
int head[MAX];
int dfn[MAX];
int low[MAX];
int vis[MAX];
int tot,Index,cnt;
void init(){
mem(head,-1);
mem(dfn,0);
mem(low,0);
mem(vis,0);
tot=0;
Index=0;
cnt=0;
}
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++Index;
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!vis[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]) vis[u]++;
}
else low[u]=min(dfn[v],low[u]);
}
if(fa==-1) node[cnt++]=(Node){u,vis[u]-1};
if(fa!=-1) node[cnt++]=(Node){u,vis[u]};
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)&&n){
int a,b;
init();
while(scanf("%d%d",&a,&b)){
if(a==-1&&b==-1) break;
add_edge(a,b);
add_edge(b,a);
}
for(int i=0;i<n;i++){
if(!dfn[i]) tarjan(i,-1);
}
sort(node,node+cnt);
for(int i=0;i<m;i++){
printf("%d %d\n",node[i].id,node[i].num);
}
printf("\n");
}
return 0;
}
uva 11294;
题意:新郎新娘是0h 0w,然后给你n-1对cp,还有其中m对有奸情(基情),然后新娘不能看见坐在她对面一排的人有奸情,输出新娘这一排的人
题解:百度了才知道是2-SAT,学了一下午,自己写tarjan的拓扑排序写不像,然后就用了大白的上面的模版(lrj大神就是厉害)
首先是建图的问题,2-SAT的具体思想就看博客或者大白好叻
一对cp为i,拆成两个点2*i和2*i+1,女的坐在新娘这一排的为真,记作2*i,男的坐在新娘这一排为假,记作2*i+1
如果输入的是xw yw,就是两个女的有奸情,她们之间必须至少有一个在新娘这排 就是 x V y,(A是且)等价于 ~x=>y A ~y=>x
就在2*x+1 ->2*y和2*y+1->2*x之间建有向边
搜的时候先搜2*i,假设i是真,如果搜到某一对节点都在其中,有一个环,那么就是不可能的,返回false,说明i为真是错的,就搜2*i+1,如果还是错的,那么就是不存在这种分配的方法
wa了次,因为没考虑到有可能有人和新郎新娘有奸情(这尼玛太扯淡)
坐在新娘这排肯定是真,那么新娘0肯定是真,新郎1为假,如果dfs(0)返回false,说明新娘不为真,这可以直接说明不存在正确的分法
这题就当熟悉2-SAT模版
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 2005
#define MAXN 40005
#define maxnode 1005
#define sigma_size 4
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
const LL mod = 9901;
/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}
while(isdigit(tmp=getchar()));
return ret;
}
/*******************************************/
struct TwoSAT{
int n;
struct Edge{
int v,next;
}edge[MAXN];
int head[MAX];
int tot;
bool mark[MAX];
int S[MAX];
int c;
bool dfs(int x){
if(mark[x^1]) return false;
if(mark[x]) return true;
mark[x]=true;
S[c++]=x;
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!dfs(v)) return false;
}
return true;
}
void init(int n){
this->n=n;
mem(head,-1);
mem(mark,0);
tot=0;
}
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
bool solve(){
for(int i=0;i<n*2;i+=2){
if(!mark[i]&&!mark[i+1]){
c=0;
if(!dfs(i)){
if(i==0) return false;
while(c>0) mark[S[--c]]=false;
if(!dfs(i+1)) return false;
}
}
}
return true;
}
}sat;
char get[30];
int main(){
int n,m;
while(scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
sat.init(n);
for(int i=0;i<m;i++){
int a,b;
char c,d;
scanf("%d%c%d%c",&a,&c,&b,&d);
if(c=='h'&&d=='h'){
sat.add_edge(2*a,2*b+1);
sat.add_edge(2*b,2*a+1);
}
if(c=='w'&&d=='h'){
sat.add_edge(2*a+1,2*b+1);
sat.add_edge(2*b,2*a);
}
if(c=='h'&&d=='w'){
sat.add_edge(2*a,2*b);
sat.add_edge(2*b+1,2*a+1);
}
if(c=='w'&&d=='w'){
sat.add_edge(2*a+1,2*b);
sat.add_edge(2*b+1,2*a);
}
}
if(sat.solve()){
for(int i=1;i<n;i++){
if(sat.mark[2*i]) printf("%dw",i);
else printf("%dh",i);
if(i==n-1) printf("\n");
else printf(" ");
}
}
else printf("bad luck\n");
}
return 0;
}
uva 11972
题意:给你一个无向图,然后问你要添加多少条边才能让这个图变成双联通
题解:这题和以前做过的不一样的地方在于:原本给的图不一定是连通的,有可能缩点之后会有孤立点,其实也就最后的公式和以前的不一样
缩点之后,度为1的点为叶子节点有A个,度为0的节点有B个,公式就是(A+1)/2+B,如果图原本就是双连通的,那么答案就是0(因为这个wa了好几次,因为如果原本就是双连通的,B=1,那么公式答案就是1,就跪了)
公式的话自己画图看看就行了(A+1)/2是原本如果只有叶子节点的公式,如果存在一些孤立点,只要把孤立点,塞进你两个叶子节点连接的边之间(就等于多加一条边)
果然一边打游戏一边写代码正确率很低,而且窝在宿舍这种小地方写代码正确率确实不行,还是喜欢宽敞的组里啊
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1005
#define MAXN 200005
#define maxnode 1005
#define sigma_size 4
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
const LL mod = (1<<64);
/**************¶áèëía1ò*********************/
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}
while(isdigit(tmp=getchar()));
return ret;
}
/*******************************************/
struct Edge{
int v,next;
}edge[MAX*MAX];
int head[MAX];
int dfn[MAX];
int low[MAX];
int instack[MAX];
int sstack[MAX];
int belong[MAX];
int in[MAX];
int tot,Index,cnt,top;
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
void init(){
mem(head,-1);
mem(dfn,0);
mem(low,0);
mem(in,0);
mem(belong,0);
mem(instack,0);
top=0;
cnt=0;
tot=0;
Index=0;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++Index;
instack[u]=1;
sstack[++top]=u;
int flag=0;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa&&!flag){
flag=1;
continue;
}
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[v],low[u]);
}
else if(instack[v]) low[u]=min(dfn[v],low[u]);
}
if(low[u]==dfn[u]){
cnt++;
while(1){
int k=sstack[top--];
instack[k]=0;
belong[k]=cnt;
if(k==u) break;
}
}
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
init();
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
add_edge(a,b);
add_edge(b,a);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i,-1);
}
if(cnt==1){
printf("0\n");
continue;
}
for(int i=1;i<=n;i++){
for(int j=head[i];j!=-1;j=edge[j].next){
int v=edge[j].v;
if(belong[i]!=belong[v]){
in[belong[i]]++;
}
}
}
int ans1=0,ans2=0;
for(int i=1;i<=cnt;i++){
if(in[i]==1) ans1++;
if(in[i]==0) ans2++;
}
printf("%d\n",(ans1+1)/2+ans2);
}
return 0;
}