残暴的逊哥又来血腥镇压我们了,然后给我们布置了很多图论题
有不少也是我没做过的,而且还学了不少新东西,撸完要好好记录一发
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; }