@darkproject
2018-02-14T14:10:59.000000Z
字数 7952
阅读 901
2018寒假集训
A - The Necklace (UVA - 10054)
每个珠子有2半颜色,问是否能将珠子串起来形成一个环链(珠子之间连接必须保证连接处颜色相同),将每种颜色看作结点,珠子2半连一条有向边,问题可以转换欧拉回路输出路径。因为珠子可以翻转,可以当作无向图的欧拉回路来做。
以下为存在欧拉回路的条件:(图必须连通)
在无向图中,所有顶点的度数均为偶,则存在 Eularian cycle;若有且仅有两个顶点的度数为奇,其余的都为偶,则存在 Eularian path;
在有向图中,所有顶点的入度数等于出度数,则存在 Eularian cycle;若有且仅有两个顶点:其中一个入度数比出度数大 1,另一个入度数比出度数小 1,其余的顶点入度数等于出度数,则存在 Eularian path.
综上并查集判断图是否连通,fleury求欧拉回路
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn =55;
int t,n;
int par[maxn],deg[maxn],mp[maxn][maxn];
void init()
{
for(int i=1;i<=50;i++)
par[i]=i;
memset(deg,0,sizeof(deg));
memset(mp,0,sizeof(mp));
}
int find(int x)
{
if(x==par[x])
return x;
else
return par[x]=find(par[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
par[x]=y;
}
void dfs(int x)
{
for(int i=1;i<=50;i++)
{
if(mp[x][i]>=1)
{
mp[x][i]--;
mp[i][x]--;
dfs(i);
printf("%d %d\n",i,x);
}
}
}
int main()
{
int cnt=0;
scanf("%d",&t);
while(t--)
{
int a,b;
int mxr=0;
int hyx=0;
scanf("%d",&n);
init();
for(int i=0;i<n;i++)
{
scanf("%d%d",&a,&b);
mp[a][b]++;
mp[b][a]++;
deg[a]++;
deg[b]++;
unite(a,b);
}
printf("Case #%d\n",++cnt);
for(int i=1;i<=50;i++)
{
if(deg[i]==0) continue;
if(deg[i]&1) mxr++;
if(par[i]==i) hyx++;
}
if(mxr==0&&hyx==1)
{
for(int i=1;i<=50;i++)
if(deg[i]!=0)
dfs(i);
}
else
printf("some beads may be lost\n");
printf("\n");
}
return 0;
}
B - Guess (UVALive - 4255)
给出一个符号矩阵S,求满足符号矩阵的序列,序列中整数的范围[-10,10]。S(i,j)=ai+...+aj。利用前缀和可以得出sum(i)-sum(j)(>,<,=)0的关系将其前缀和看作结点,已知sum(i)与sum(j)的关系建图,转化为拓扑排序问题,得出满足sum关系的序列最后求出要求序列的每个整数。ps:=0的情况作为度为0的点直接入队列
我们只需要求出满足条件的任意一组解,因为要保证要求序列中每个整数的范围为-10到10,我们sum给值的时候应给[-10,0]或者[0,10]的n个
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 15;
int t,n,deg[maxn],sum[maxn];
vector<int>G[maxn];
queue<int>q;
void init()
{
for(int i=0;i<=n;i++)
{
G[i].clear();
}
memset(deg,0,sizeof(deg));
memset(sum,0,sizeof(sum));
}
void bfstop()
{
int num=0;
for(int i=0;i<=n;i++)
{
if(deg[i]==0){
q.push(i);
sum[i]=num;
}
}
while(!q.empty())
{
int p=q.front();
q.pop();
num++;
for(int j=0;j<G[p].size();j++)
{
--deg[G[p][j]];
if(deg[G[p][j]]==0)
{
q.push(G[p][j]);
sum[G[p][j]]=num;
}
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
for(int i=0;i<n;i++)
for(int j=i+1;j<=n;j++)
{
char ch;
scanf(" %c",&ch);
if(ch=='+'){
G[i].push_back(j);
deg[j]++;
}
else if(ch=='-'){
G[j].push_back(i);
deg[i]++;
}
}
bfstop();
for(int i=1;i<n;i++)
printf("%d ",sum[i]-sum[i-1]);
printf("%d\n",sum[n]-sum[n-1]);
}
return 0;
}
C - Claw Decomposition (UVA - 11396)
题意可以简单概括为给出一个图,问是否能将图分割为若干个爪的图案,且每条边只属于一个爪。
分析出爪中间的点是不会与另外爪的中间点相连的,且爪所连的三个点与另外的爪所连的三个点也是不会相连的。因此满足条件的图是一个二分图,这里我们只需要染色法进行二分图判定即可。爪如下:
O
|
O
/ \
O O
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 305;
int n,color[maxn];
vector<int>G[maxn];
void init()
{
for(int i=1;i<=n;i++)
G[i].clear();
}
bool judge(int v,int c)
{
color[v]=c;
for(int i=0;i<G[v].size();i++)
{
if(color[G[v][i]]==c) return false;
if(color[G[v][i]]==0&&!judge(G[v][i],-c)) return false;
}
return true;
}
bool solve()
{
memset(color,0,sizeof(color));
for(int i=1;i<=n;i++)
{
if(color[i]==0)
if(!judge(i,1))
return false;
}
return true;
}
int main()
{
while(cin>>n)
{
if(n==0) break;
int a,b;
init();
while(cin>>a>>b)
{
if(a==0&&b==0) break;
G[a].push_back(b);
G[b].push_back(a);
}
if(solve()) printf("YES\n");
else printf("NO\n");
}
return 0;
}
D - Cells (UVALive - 3486)
树多次询问a是否是b的祖先,采用蓝书上时间戳的做法前序后后序遍历存一个pre数组和post数组,祖先的pre数组一定比子结点的pre小,post数组一定比子结点大,我们只需要判断这点即可。ps:这里不能寻常的递归dfs,需要用栈来模拟。因为树深度很大,递归会导致RE
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int maxn = 2e7+100;
int t,n,m,dfs_clock;
int pre[maxn],post[maxn];
vector<int>G[maxn];
int vis[maxn];
stack<int>mxr;
void init()
{
memset(vis,0,sizeof(vis));
for(int i=0;i<maxn;i++)
G[i].clear();
memset(pre,0,sizeof(pre));
memset(post,0,sizeof(post));
}
void dfs()
{
mxr.push(0);
while(!mxr.empty())
{
int now=mxr.top();
if(!vis[now])
{
vis[now]=1;
pre[now]=++dfs_clock;
for(int i=0;i<G[now].size();i++)
{
int v=G[now][i];
// if(!vis[v])
// mxr.push(v);
if(v<n) mxr.push(v);
else{
pre[v]=++dfs_clock;
post[v]=++dfs_clock;
}
}
}
else{
mxr.pop();
post[now]=++dfs_clock;
}
}
}
int main()
{
int cnt=0;
int ok=0;
scanf("%d",&t);
while(t--)
{
int j=1;
dfs_clock=0;
if(ok) printf("\n");ok=1;
scanf("%d",&n);
init();
for(int i=0;i<n;i++)
{
int temp;
scanf("%d",&temp);
for(j;temp>0;j++,temp--)
{
G[i].push_back(j);
}
}
dfs();
scanf("%d",&m);
printf("Case %d:\n",++cnt);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
if(pre[a]<pre[b]&&post[a]>post[b])
printf("Yes\n");
else
printf("No\n");
}
//printf("\n");
}
return 0;
}
H - Proving Equivalences (UVALive - 4287)
定理证明,给予几个定理及其证明关系,求至少还需要证明那几个关系才能保证这几个定理之间可以相互推导。题意可以转换为n结点m条边的有向图,添加最少的有向边使得整个图强连通。强连通缩点后统计入度和出度为0的点,ans=max(入度0,出度0)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 20005;
vector<int>G[maxn];
vector<int>rG[maxn];
vector<int>vs;
bool used[maxn];
int cmp[maxn],in[maxn],out[maxn];
int t,n,m;
void init()
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(cmp,0,sizeof(cmp));
for(int i=1;i<=n;i++)
{
G[i].clear();
rG[i].clear();
}
}
void dfs(int v)
{
used[v]=true;
for(int i=0;i<G[v].size();i++)
{
int u=G[v][i];
if(!used[u])
dfs(u);
}
vs.push_back(v);
}
void rdfs(int v,int k)
{
used[v]=true;
cmp[v]=k+1;
for(int i=0;i<rG[v].size();i++)
{
int u=rG[v][i];
if(!used[u])
rdfs(u,k);
}
}
int scc()
{
memset(used,0,sizeof(used));
vs.clear();
for(int i=1;i<=n;i++)
if(!used[i])
dfs(i);
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1;i>=0;i--)
if(!used[vs[i]])
rdfs(vs[i],k++);
return k;
}
int main()
{
scanf("%d",&t);
while(t--)
{
int a,b,ans,mxr,hyx;
ans=mxr=hyx=0;
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
rG[b].push_back(a);
}
int scc_cnt=scc();
for(int v=1;v<=n;v++)
for(int j=0;j<G[v].size();j++)
{
int u=G[v][j];
if(cmp[u]!=cmp[v]) {//本来就处于强连通的点不考虑
//cmp表示缩点后的点的拓扑序
in[cmp[u]]++;
out[cmp[v]]++;
}
}
for(int i=1;i<=scc_cnt;i++){
if(in[i]==0) mxr++;
if(out[i]==0) hyx++;
}
//cout<<mxr<<"*"<<hyx<<endl;
ans=max(mxr,hyx);
if(scc_cnt==1)
ans=0;
printf("%d\n",ans);
}
return 0;
}
J - Now or later (UVALive - 3211)
设早着陆的时间为E,晚着陆的时间为L。有n架飞机着陆, 飞机按实际着陆时间排序,相邻两个着陆时间间隔的最小值要尽量大。输出这个间隔最大值。最小值最大考虑二分答案,设答案为p,即相邻两个时间不小于p。即任意两个飞机的着陆时间不小于p。设布尔变量表示第i架飞机早着陆,根据条件我们可以得到以下几种冲突关系与,与,与,与.于是我们可以列出如下布尔表达式:是一个2-sat问题,要满足题目所给条件即使布尔表达式值为真,我们根据这个表达式建图。根据白书方法建立图,原布尔表达式可以转化为如下表达式:其中表示由a向b连边
利用scc求解(如果和x在同一强连通分量的话则无法令整个布尔表达式为真,且对于每个布尔变量x所在的强连通分量的拓扑序在所在的连通分量之后可以得到x布尔变量为真)是否有满足布尔表达式的一组布尔变量来进行二分。复杂度O()
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 4225;
vector<int>G[maxn];
vector<int>rG[maxn];
vector<int>vs;
bool used[maxn];
int cmp[maxn];
int n,early[maxn],late[maxn];
void init()
{
for(int i=0;i<=n*2;i++)
{
G[i].clear();
rG[i].clear();
}
memset(cmp,-1,sizeof(cmp));
}
void add(int from,int to)
{
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v)
{
used[v]=true;
for(int i=0;i<G[v].size();i++)
{
int u=G[v][i];
if(!used[u])
dfs(u);
}
vs.push_back(v);
}
void rdfs(int v,int k)
{
used[v]=true;
cmp[v]=k;
for(int i=0;i<rG[v].size();i++)
{
int u=rG[v][i];
if(!used[u])
rdfs(u,k);
}
}
int scc()
{
memset(used,0,sizeof(used));
vs.clear();
for(int v=0;v<n;v++)
if(!used[v]) dfs(v);
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1;i>=0;i--)
if(!used[vs[i]]) rdfs(vs[i],k++);
return k;
}
bool judge(int x)
{
init();
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
{
if(abs(early[i]-early[j])<x){
add(i,n+j);
add(j,n+i);
}
if(abs(late[i]-late[j])<x){
add(n+i,j);
add(n+j,i);
}
if(abs(early[i]-late[j])<x){
add(i,j);
add(n+j,n+i);
}
if(abs(late[i]-early[j])<x){
add(n+i,n+j);
add(j,i);
}
}
scc();
for(int i=0;i<n;i++)
{
if(cmp[i]==cmp[n+i])
return false;
}
return true;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
init();
int l,r;
l=r=0;
for(int i=0;i<n;i++)
{
scanf("%d%d",&early[i],&late[i]);
r=max(r,max(early[i],late[i]));
}
int mxr;
while(l<=r)
{
int m=(l+r)>>1;
// cout<<m<<endl;
if(judge(m))
{
mxr=m;
l=m+1;
}
else
r=m-1;
}
printf("%d\n",mxr);
}
return 0;
}