• USACO DEC 2019 参赛总结


    USACO DEC 2019 参赛总结(2019-12-13~2019-12-16)

    2019-12-19 xiaoh

    金组Gold

    A-Milk Pumping

    题意

    给定一个n个点,m条边的无向图,每一条边都有一个最大流量f和一个价格c(1<=n,m<=1000,1<=c,f<=1000),找一条从1到n的路径(保证存在),求最大化Min{f}/Σ{c}的路径,将这个值×1e6取下整后输出

    题解

    首先,很明显,当流量最小值固定时,我们只要求满足条件的最小的Σ{c}即可。所以考虑枚举每一个Min{f},然后将所有流量>=f的边建图,跑Dijkstra即可,时间复杂度O(maxf×(n+m)lg(n+m)),大约在1e7多一点的样子,可以过,敲板子即可。

    Code

    #include<bits/stdc++.h>
    #define inf 9999999
    using namespace std;
    inline int read()//快读
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    int n,m;
    int tot=1;
    struct node{
    	int v,c;
    	bool flag;
    }edge[2010];
    int nxt[2010];
    int hd[1010];
    inline void add_edge(int u,int v,int c)//前向星
    {
    	edge[tot].v=v,edge[tot].c=c;
    	nxt[tot]=hd[u];
    	hd[u]=tot++;
    }
    struct node2{
    	int u,v,c,f;
    }a[1010];
    bool cmp(node2 a,node2 b)//将边按流量排序(实际没什么卵用)
    {
    	return a.f<b.f;
    }
    struct node3{//Dijkstra专用堆+运算符重载
    	int pos,num;
    	friend bool operator < (node3 a,node3 b)
    	{
    		return a.num>b.num;
    	}
    };
    long double ans=0;
    priority_queue<node3> q;
    int dis[1010];
    bool book[1010];
    inline void init(int limit)//按照流量限制初始化并建图
    {
    	tot=1;
    	memset(edge,0,sizeof(edge));
    	memset(nxt,0,sizeof(nxt));
    	memset(hd,0,sizeof(hd));
    	memset(book,0,sizeof(book));
    	for(int i=1;i<=n;i++) dis[i]=(i==1)? 0:inf;
    	for(int i=1;i<=m;i++)
    	if(a[i].f>=limit)
    	{
    		add_edge(a[i].u,a[i].v,a[i].c);
    		add_edge(a[i].v,a[i].u,a[i].c);
    	}
    }
    void Dijkstra()//Dijkstra板子,不解释
    {
    	q.push({1,0});
    	while(!q.empty())
    	{
    		int x=q.top().pos;
    		q.pop();
    		if(book[x]) continue;
    		book[x]=1;
    		for(int i=hd[x];i;i=nxt[i])
    		if(dis[edge[i].v]>dis[x]+edge[i].c)
    		{
    			dis[edge[i].v]=dis[x]+edge[i].c;
    			q.push({edge[i].v,dis[edge[i].v]});
    		}
    	}
    }
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<=m;i++) a[i].u=read(),a[i].v=read(),a[i].c=read(),a[i].f=read();
    	sort(a+1,a+m+1,cmp);
    	for(int i=1;i<=1000;i++)
    	{
    		init(i);
    		Dijkstra();
    		ans=max(ans,(long double)i/(long double)dis[n]);
    	}
    	ans*=(long double)1000000;
    	ans=floor(ans);
    	printf("%lld\n",(long long)ans);
    	return 0;
    }
    

    B-Milk Visits

    题意

    给定一棵有n个节点的树(1<=n<=1e5),每个节点上都有特定种类的牛(种类在[1,n]间),有m个询问,每次给一个ai,bi表示树上的一条在ai,bi间的路径,问路径中是否存在至少一头种类为ci的牛

    题解

    笔者刚看到题的时候,第一个反应是用LCA维护啊!用LCA暴力维护每一种牛是否存在,至于维护的具体方式只能是bitset之类的数据结构,可就算是使用bitset,我们可以大致的计算一下空间复杂度:1e5×lg1e5×1e5/32,大概是20个G,根本不行!所以只能换思路了。
    由于题目没有强制要求询问在线,因此可以考虑将询问离线处理。首先观察询问的特点,发现每个询问只涉及一种牛,所以我们可以考虑先假设整棵树是空的,然后往里加入种类为i的牛,处理对应查询,再把这些牛删掉。这样每次只需要在节点上维护一个bool值,表示当前位置是否有一头当前正在处理的种类的牛即可,而不必单独维护每一种牛是否存在,很大的简化了问题。但是这个做法有一个很大的缺陷:你需要用一个数据结构,使它能够支持动态修改和查询,而前面提到的LCA在查询上表现相当优异,在面对修改时却退化成了O(nlgn)的复杂度,远远不能支持算法。所以我们要考虑更适合的数据结构。
    树,修改,查询路径权值……想必各位也想到了,树链剖分是最适合的。所以算法便自然而然的出来了:先将空树剖分,然后每个节点维护一个bool值,表示当前位置是否有一头当前正在处理的种类的牛,用线段树维护重链上是否至少存在一个true值。每次处理询问前,将对应种类上的对应位置的牛的bool值改为true,处理对应查询,并将对应位置上的牛的bool值重新改为false即可。时间复杂度O(nlgnlgn),大约在3×1e7左右,刚好可以卡过去。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()//快读
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    int n,m;
    int tot=1;
    int edge[200010];
    int nxt[200010];
    int hd[100010];
    inline void add_edge(int u,int v)//前向星
    {
    	edge[tot]=v;
    	nxt[tot]=hd[u];
    	hd[u]=tot++;
    }
    int a[100010];
    bool ans[100010];
    //Segment_Tree
    struct node{
    	int l,r;
    	bool num;
    }f[100010*4];
    inline void pushup(int p)
    {
    	f[p].num=0;//注意这一步,否则节点有可能一直是true而在其子节点都被改为false后由于or的性质没有被改变
    	f[p].num|=f[p*2].num,f[p].num|=f[p*2+1].num;
    }
    void build(int p,int l,int r)//建树
    {
    	f[p].l=l,f[p].r=r;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	build(p*2,l,mid);
    	build(p*2+1,mid+1,r);
    }
    bool query(int p,int l,int r)//查询
    {
    	if(f[p].l>=l&&f[p].r<=r) return f[p].num;
    	int mid=(f[p].l+f[p].r)>>1;
    	bool ans=0;
    	if(l<=mid) ans|=query(p*2,l,r);
    	if(r>mid) ans|=query(p*2+1,l,r);
    	return ans;
    }
    void modify(int p,int x,bool y)//修改
    {
    	if(f[p].l==f[p].r)
    	{
    		f[p].num=y;
    		return;
    	}
    	int mid=(f[p].l+f[p].r)>>1;
    	if(x<=mid) modify(p*2,x,y);
    	else modify(p*2+1,x,y);
    	pushup(p);
    }
    //树剖
    int son[100010],rk[100010],id[100010],sz[100010],fa[100010],top[100010],d[100010];
    void dfs1(int p,int f,int dep)//第一次深搜
    {
    	fa[p]=f,d[p]=dep,sz[p]=1;
    	for(int i=hd[p];i;i=nxt[i])
    	{
    		if(edge[i]==f) continue;
    		dfs1(edge[i],p,dep+1);
    		sz[p]+=sz[edge[i]];
    		if(sz[edge[i]]>sz[son[p]]) son[p]=edge[i];
    	}
    }
    int cnt=0;
    void dfs2(int p,int tp)//第二次深搜
    {
    	id[p]=++cnt,rk[cnt]=p,top[p]=tp;
    	if(son[p]) dfs2(son[p],tp);
    	for(int i=hd[p];i;i=nxt[i])
    	{
    		if(edge[i]==fa[p]||edge[i]==son[p]) continue;
    		dfs2(edge[i],edge[i]);
    	}
    }
    bool query_LCA(int x,int y)//查询答案
    {
    	bool ans=0;
    	while(top[x]!=top[y])
    	{
    		if(d[top[x]]<d[top[y]]) swap(x,y);
    		ans|=query(1,id[top[x]],id[x]);
    		x=fa[top[x]];
    	}
    	ans|=query(1,min(id[x],id[y]),max(id[x],id[y]));
    	return ans;
    }
    vector<int> v1[100010];//储存每种牛在哪些位置
    struct node2{
    	int a,b,id;
    };
    vector<node2> v2[100010];//储存查询每种牛的查询操作在哪个位置,方便最后储存答案
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<=n;i++) a[i]=read(),v1[a[i]].push_back(i);
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		add_edge(u,v),add_edge(v,u);
    	}
    	dfs1(1,-1,1);
    	dfs2(1,1);
    	build(1,1,n);
    	for(int i=1;i<=m;i++)
    	{
    		int a=read(),b=read(),c=read();
    		v2[c].push_back({a,b,i});
    	}
    	for(int i=1;i<=n;i++)
    	{
    		for(vector<int>::iterator it=v1[i].begin();it!=v1[i].end();it++) modify(1,id[*it],1);
    		for(vector<node2>::iterator it=v2[i].begin();it!=v2[i].end();it++) ans[it->id]=query_LCA(it->a,it->b);
    		for(vector<int>::iterator it=v1[i].begin();it!=v1[i].end();it++) modify(1,id[*it],0);
    	}
    	for(int i=1;i<=m;i++)
    	if(ans[i]) printf("1");
    	else printf("0");
    	printf("\n");
    	return 0;
    }
    

    C-Moortal Cowmbat

    P.S. 由于笔者水平不够,只能写出O(n^2)的算法,然鹅过不了此题,就不放在这里丢人现眼了QAQ(虽然我已经看出这道题是四边形不等式优化DP了)

    银组

    A-MooBuzz

    题意

    从1开始报数,报到3或5的倍数就跳过去,求第n个报到的数是多少(1<=n<=1e9)

    题解

    签到题。显然,每15个数一循环,只有1,2,4,7,8,11,13,14可以取,所以直接枚举出现了几个完整的循环并统计余数即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    long long n;
    long long a[]={-1,1,2,4,7,8,11,13};
    int main()
    {
    	cin>>n;
    	cout<<(n/8)*15+a[n%8]<<endl;
    	return 0;
    }
    

    B-Meetings

    题意

    有n头牛在一根长度为l的数轴上(1<=n<=5×1e4,1<=l<=1e9),每头牛要么朝左,要么朝右,都按照1的单位每秒的速度运动,当运动到0或l时就会停下。当两头牛在同一个点上时(即相遇,未必在整点上发生),他们会原路返回(即反弹)。每头牛都有一个质量m(1<=m<=1e3),当停下牛的质量>=总质量的一半时,求相遇牛的对数。

    题解

    先喷一句,不愧是Benjamin Qi出的题,果然够毒瘤,恶心了我十几分钟才想出正解。真是不折不扣的毒瘤
    首先,将算法分为2部分。第一部分:求停下的时间t;第二部分:求相遇牛的对数。先考虑第一部分。如果有人做过POJ的一道题Ants的话,就会发现这道题就是它的升级版。当两头牛反弹时,我们可以理解为两头牛互相穿过对方,继续前进,然后得出一个序列a,其中ai表示第i头停下的牛(注意不是第i头牛,是第i头停下的牛)是在最左边停的还是在最右边停的。这一步想必各位都能写的出来,排个序一算就好了。然后考虑题目的现状:两头牛相遇后反弹,此时不难发现,此时得到的a序列与将“相遇”理解为“穿过”时的a序列竟然完全相同!(如果想不通的话自己手动模拟一下就好啦)然后再考虑:每头停下的牛,若他是在最左边停下的,那么说明他的左边一定没有牛,右边同理。所以我们先假设“相遇”就是“穿过对方”,求出a序列,再将每头牛按照位置排序,由于停下的牛一定在两端,所以我们就可以得到停下牛的顺序,从而求出相应的停下时间t。
    再考虑第二部分:相遇对数。有了前面的t,这一步就相当简单了,你可以将这种情况理解为逆序对。先统计初始状态下每头牛的位置上,从左到右重新按1~n编号(在代码中的体现就是排序),然后再统计t秒后每头牛的位置。由于同向的牛不会发生追击,所以很明显我们不必考虑停下的状况,假设他们一直运动的结果和到两端停下的结果一定相同。逆序对求解即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    int n,l;
    struct node{
    	int w,x,d;
    }a[50010];
    bool cmp(node x,node y)
    {
    	return x.x<y.x;
    }
    struct node2{
    	int t,dir;
    }b[50010];
    bool cmp2(node2 x,node2 y)
    {
    	return x.t<y.t;
    }
    int totw=0,t;
    int f[50010];//树状数组板子
    inline int lowbit(int x)
    {
    	return x&(-x);
    }
    inline void modify(int x,int y)
    {
    	for(;x<=n;x+=lowbit(x)) f[x]+=y;
    }
    inline int query(int x)
    {
    	int ret=0;
    	for(;x;x-=lowbit(x)) ret+=f[x];
    	return ret;
    }
    struct node3{
    	int pos,id;
    }c[50010];
    bool cmp3(node3 x,node3 y)
    {
    	if(x.pos!=y.pos) return x.pos<y.pos;
    	else return x.id>y.id;//注意:有可能有多头牛最后在同一个点,此时他们也算相遇,所以要加这一句
    }
    long long ans=0;
    int main()
    {
    	n=read(),l=read();
    	for(int i=1;i<=n;i++) a[i].w=read(),a[i].x=read(),a[i].d=read(),totw+=a[i].w;
    	sort(a+1,a+n+1,cmp);//按照数轴上的位置排序
    	for(int i=1;i<=n;i++)
    	if(a[i].d==1) b[i]={l-a[i].x,1};
    	else b[i]={a[i].x,-1};//按照“互相穿过”计算A序列
    	sort(b+1,b+n+1,cmp2);
    	int lp=1,rp=n,cnt=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(b[i].dir==1) cnt+=a[rp--].w;
    		else cnt+=a[lp++].w;
    		t=b[i].t;
    		if(cnt>=(totw+1)/2) break;
    	}//计算时间t
    	for(int i=1;i<=n;i++)
    	if(a[i].d==1) c[i]={a[i].x+t,i};
    	else c[i]={a[i].x-t,i};//计算t秒后的位置
    	sort(c+1,c+n+1,cmp3);//排序
    	for(int i=n;i>=1;i--)
    	ans+=(long long)query(c[i].id-1),modify(c[i].id,1);//逆序对
    	printf("%lld\n",ans);
    	return 0;
    }
    

    C-Milk Visits(弱化版)

    题意

    题面与金组T2类似。给定一棵有n个节点的树(1<=n<=1e5),每个节点上都有特定种类的牛,种类为Guernsey(用'G'表示)或Holstein(用'H'表示),有m个询问,每次给一个ai,bi表示树上的一条在ai,bi间的路径,问路径中是否存在至少一头种类为ci的牛(ci是'G'或'H'中的一个)

    题解

    是金组T2的魔改版,基本属于模板题。利用LCA维护整颗树,并在维护LCA时同时维护两个bool变量,表示目前的这一段是否有至少一头H牛和目前的这一段是否至少有一头G牛,然后打LCA的板子即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    int n,m;
    int a[100010];
    int tot=1;
    int edge[200010];
    int nxt[200010];
    int hd[100010];
    inline void add_edge(int u,int v)//前向星
    {
    	edge[tot]=v;
    	nxt[tot]=hd[u];
    	hd[u]=tot++;
    }
    int d[100010];
    struct node{
    	int pos;
    	bool val1,val2;
    }LCA[100010][20];
    queue<int> q;
    bool book[100010];
    int max_lg;
    void prework()//LCA预处理
    {
    	book[1]=1;
    	d[1]=1,q.push(1);
    	max_lg=log2(n)+1;
    	while(!q.empty())
    	{
    		int x=q.front();
    		q.pop();
    		for(int i=hd[x];i;i=nxt[i])
    		{
    			int y=edge[i];
    			if(book[y]) continue;
    			book[y]=1;
    			if(a[x]==0) LCA[y][0].val1=1;
    			else LCA[y][0].val2=1;
    			LCA[y][0].pos=x;
    			d[y]=d[x]+1;
    			for(int j=1;j<=max_lg;j++)
    			{
    				LCA[y][j].pos=LCA[LCA[y][j-1].pos][j-1].pos;//在维护LCA的同时维护val1和val2
    				LCA[y][j].val1|=LCA[LCA[y][j-1].pos][j-1].val1;
    				LCA[y][j].val1|=LCA[y][j-1].val1;
    				LCA[y][j].val2|=LCA[LCA[y][j-1].pos][j-1].val2;
    				LCA[y][j].val2|=LCA[y][j-1].val2;
    			}
    			q.push(y);
    		}
    	}
    }
    bool query(int x,int y,int type)//查询LCA的同时查询val1或val2
    {
    	bool ans1=0,ans2=0;
    	if(a[x]==0) ans1=1;
    	else ans2=1;
    	if(a[y]==0) ans1=1;
    	else ans2=1;
    	if(d[x]<d[y]) swap(x,y);
    	for(int i=max_lg;i>=0;i--)
    	if(d[LCA[x][i].pos]>=d[y])
    	{
    		ans1|=LCA[x][i].val1,ans2|=LCA[x][i].val2;
    		x=LCA[x][i].pos;
    	}
    	if(x==y)
    	{
    		if(type==1) return ans1;
    		else return ans2;
    	}
    	for(int i=max_lg;i>=0;i--)
    	if(LCA[x][i].pos!=LCA[y][i].pos)
    	{
    		ans1|=LCA[x][i].val1,ans2|=LCA[x][i].val2;
    		x=LCA[x][i].pos;
    		ans1|=LCA[y][i].val1,ans2|=LCA[y][i].val2;
    		y=LCA[y][i].pos;
    	}
    	ans1|=LCA[x][0].val1,ans2|=LCA[x][0].val2;
    	ans1|=LCA[y][0].val1,ans2|=LCA[y][0].val2;
    	if(type==1) return ans1;
    	else return ans2;
    }
    int main()
    {
    	n=read(),m=read();
    	string s;
    	cin>>s;
    	for(int i=0;i<s.length();i++)
    	if(s[i]=='H') a[i+1]=0;
    	else a[i+1]=1;
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	prework();
    	for(int i=1;i<=m;i++)
    	{
    		int x=read(),y=read();
    		char c;
    		cin>>c;
    		if(c=='H')
    		{
    			if(query(x,y,1)==0) printf("0");
    			else printf("1");
    		}
    		else
    		{
    			if(query(x,y,2)==0) printf("0");
    			else printf("1");
    		}
    	}
    	printf("\n");
    	return 0;
    }
    

    铜组

    A-Cow Gymnastics

    题意

    有n头牛,k次训练课。给出每次训练课每头牛的排名。求满足所有训练课中,一头牛都比另一头排名靠前的牛的对数(1<=k<=10,1<=n<=20)

    题解

    暴力枚举即可,怎么样写都能过,就不细讲了。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()//快读
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    int n,k;
    int a[25][25];
    int ans=0;
    int main()
    {
    	k=read(),n=read();
    	for(int i=1;i<=k;i++)
    	for(int j=1;j<=n;j++)//读入
    	{
    		int x=read();
    		a[i][x]=j;
    	}
    	for(int i=1;i<=n;i++)//枚举牛
    	for(int j=1;j<=n;j++)
    	if(i!=j)
    	{
    		for(int l=1;l<=k;l++)//检查是否可行
    		if(a[l][i]<a[l][j]) goto nxt;
    		ans++;
    		nxt:;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    B-Where am I?

    题意

    给一个字符串S(1<=|s|<=100,且S有大写字母构成),对于每一个位置i,我们用一段子串si,si+1,si+2,...,si+k表示它的位置。求最小的满足条件的k,使所有的位置i,表示它位置的子串各不相同

    题解

    截断子串,并保证其独一无二性,让我们想到了利用map进行查重操作。建立一个map,表示string到bool的映射。考虑暴力枚举k,并把每个字串在map中的值改为1。若该字串在修改之前已经为1,说明重复了,排除,进入下一个k进行枚举。注意map不需要实时清空,因为每个substring的长度都不同,因而不会在不同的k时出现相同的字串。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    string s;
    map<string,bool> mp;
    int main()
    {
    	cin>>n>>s;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=0;j+i-1<n;j++)
    		{
    			string tmp=s.substr(j,i);//取表示位置当前的子串
    			if(mp[tmp]) goto nxt;//若重复,则不可行,进入下一个k
    			mp[tmp]=1;//将子串标记为存在
    		}
    		cout<<i<<endl;//若没有冲突,则说明当前解可行,输出并返回
    		return 0;
    		nxt:;
    	}
    	return 0;
    }
    

    C-Line Up

    题意

    有8头牛,分别叫,要排成一列。给定n个限制条件(1<=n<=7),每个条件为"A must be milked after B",表示A、B必须在队伍中相邻。求满足条件的字典序最小的排列

    题解

    个人认为是三场比赛中最最最无耻的一道题,差点逼我用拓扑(虽然拓扑也做不出来),尤其对于新手相当不友好,将暴力的正解藏得严严实实,令人浮想翩翩,不知不觉进入贪心和DP的大坑QAQ。而且说是字典序,这牛名字有一大半都是B打头的,光找字典序我就差点自闭,还考了字符串的输入输出,实在是恶心到了令人发指的地步。请所有该题原地爆炸的同学仔细读一下数据,就8头牛,你觉得对于将一个数据复杂度卡的刚刚好的毒瘤OJ——USACO,怎么可能这么友好,一个复杂度n方一下的算法会只可能给大小为8的数据吗?(况且铜组里一般不会出现此类算法的)仔细揣摩,复杂度能恰好卡到8的算法,也只有O(n!×n)或O(2^n)之类了吧。所以只可能是暴力搜索。我这里采用了map映射的形式,将牛按照字典序进行哈希,然后使用STL中的next_permutation,那么第一个可行解一定是字典序最小的,只需全排列+检查即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    int a[10];
    map<string,int> mp1;
    map<int,string> mp2;
    int b[8][2];
    void check()
    {
    	for(int i=1;i<=n;i++)//检查当前解是否可行
    	{
    		int x,y;
    		for(int j=1;j<=8;j++)
    		if(a[j]==b[i][0]) x=j;
    		else if(a[j]==b[i][1]) y=j;
    		if(abs(x-y)!=1) return;
    	}
    	for(int i=1;i<=8;i++) cout<<mp2[a[i]]<<endl;//若可行,直接输出并返回
    	exit(0);
    }
    int main()
    {
    	mp1["Beatrice"]=1,mp2[1]="Beatrice";//正向+反向手动按字典序映射
    	mp1["Belinda"]=2,mp2[2]="Belinda";
    	mp1["Bella"]=3,mp2[3]="Bella";
    	mp1["Bessie"]=4,mp2[4]="Bessie";
    	mp1["Betsy"]=5,mp2[5]="Betsy";
    	mp1["Blue"]=6,mp2[6]="Blue";
    	mp1["Buttercup"]=7,mp2[7]="Buttercup";
    	mp1["Sue"]=8,mp2[8]="Sue";
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		string s;
    		cin>>s;
    		b[i][0]=mp1[s];
    		cin>>s,cin>>s,cin>>s,cin>>s;//这里连着那么多cin主要是为了处理中间的那堆废话
    		cin>>s;
    		b[i][1]=mp1[s];
    	}
    	for(int i=1;i<=8;i++) a[i]=i;
    	do{
    		check();//查询当前解是否可行
    	}while(next_permutation(a+1,a+9));//暴力枚举
    	return 0;
    }
    
  • 相关阅读:
    移动端 细节点
    基于新版 node 的 vue 脚手架搭建
    全屏展示
    Vue 小实例
    移动端 模拟键盘 盖住表单
    decodeURI decodeURIComponent
    简单时钟
    全选 反选 传统写法
    星级点评 面向过程的传统写法
    JQ字符串截取
  • 原文地址:https://www.cnblogs.com/xiaoh105/p/12116583.html
Copyright © 2020-2023  润新知