【CH Round #61 - 「Adera 10」冬令营热身赛】


吼吼~第一次rank1的比赛!


第一题直接最短路,存名字用map即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
map<string,int> mp;
string s[15],s1,s2;
int p,q,d[1000],l[1000],inq[1000],pre[1000],tot,cnt,r,h[100];
struct Reco
{
	int p,v;
}re[10000];
struct edge
{
	int y,v,ne;
}e[10000];
void Addedge(int x,int y,int v)
{
	e[++tot].y=y;
	e[tot].v=v;
	e[tot].ne=h[x];
	h[x]=tot;
}
void Ask(int from,int to)
{
	for (int i=1;i<=p;i++)
		d[i]=inf,inq[i]=0,pre[i]=0;
	queue<int> q;
	q.push(from);
	d[from]=0,inq[from]=1;
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		inq[x]=0;
		for (int i=h[x];i;i=e[i].ne)
		{
			int y=e[i].y;
			if (d[y]>d[x]+e[i].v)
			{
				d[y]=d[x]+e[i].v;
				pre[y]=x;
				l[y]=e[i].v;
				if (!inq[y]) q.push(y),inq[y]=1;
			}
		}
	}
	int u=to;
	cnt=0;
	while (u!=from)
	{
		re[++cnt].p=u;
		re[cnt].v=l[u];
		u=pre[u];
	}
	cout<<s[from];
	for (int i=cnt;i;i--)
	{
		printf("->(%d)->",re[i].v);
		cout<<s[re[i].p];
	}
	cout<<endl;
}
int main()
{
        scanf("%d",&p);
	for (int i=1;i<=p;i++)
	{
		cin>>s[i];
		mp[s[i]]=i;
	}
	int qq;
	scanf("%d",&qq);
	for (int i=1;i<=qq;i++)
	{
		int v;
		cin>>s1;cin>>s2;cin>>v;
		Addedge(mp[s1],mp[s2],v);
		Addedge(mp[s2],mp[s1],v);
	}
	scanf("%d",&r);
	for (int i=1;i<=r;i++)
	{
		cin>>s1;cin>>s2;
		Ask(mp[s1],mp[s2]);
	}
	return 0;
}


第二题类似于博弈:

分两种情况:

1.如果一共有偶数个数字,那先手必胜:

因为偶数个数字除去1这个数字后还有奇数个,那么分布在1的左右两侧的数字一定是一奇一偶的,所以先手取完之后一定可以保证1两侧都有奇数个数字;


这样到最后,1左侧一个数字,右侧一个数字,那么先手必胜直接输出1/1


2.如果一共有奇数个数字,只有1在最左侧或最右侧先手必胜:

为什么1在别的地方先手必败?因为先手取走一个后,就后手的情况就变成了第一种情况中先手的状态,所以后手必胜,直接输出2/n(注意n=1的情况要特判)


<span style="font-size:18px;">#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define LL long long
using namespace std;
LL n;
int main()
{
	cin>>n;
	if (n&1LL)
	{
		if (n==1LL) cout<<"1/1"<<endl;
		else cout<<"2/"<<n<<endl;
	}
	else
	{
		cout<<"1/1"<<endl;
	}
	return 0;
}
</span>


T3当时没看懂题,其实就是一个网络流,建图方法是:

每个点拆成两个x,x',从s向每个x连流量为1,费用为0的边;x'向t连流量为1,费用为0的边;


从x向比他线密度大的y'连流量为1费用为第一个公式的边(任何点不向1'连,n不向任何点连);


对于扭曲平面的情况,可以发现他的费用之和目的地有关,所以建立一个附加源s',s和s'连流量为m费用为0的边,s'向每个x'连流量为1费用为第二个公式的边。


求费用流即可。

(没有写= =)


第四题正解是插头dp,我用spfa水了47分:

对于只有一对的直接spfa求就可以;


有两对的枚举三种配对情况,对于要求不能有重复路径:在到达终点后把当前路径走过的地方标记,再另一对的最小值更新答案(非常暴力)


<span style="font-size:18px;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#define inf 0x3f3f3f3f
#define M 10000
using namespace std;
int nx,from,to,p[1000],ny,size,ans,pre[M],d2[M],pre2[M],inq2[M],inq[M],tot,d[M],h[M],r,c,fx[10][10];
char a[100][100],Ans[100][100];
struct edge
{
	int y,ne;
}e[M];
int C(int x,int y)
{
	return (x-1)*c+y;
}
void Addedge(int x,int y)
{
	e[++tot].y=y;
	e[tot].ne=h[x];
	h[x]=tot;
}
void Buildgragh()
{
	for (int i=1;i<=r;i++)
		for (int j=1;j<=c;j++)
		{
			for (int k=1;k<=4;k++)
			{
				int x=i+fx[k][1],y=j+fx[k][2];
				if (x>0&&y>0&&x<=r&&y<=c&&(a[x][y]==' '||a[x][y]=='X'))
					Addedge(C(i,j),C(x,y));
			}
		}
}
void Get(int x)
{
	nx=0,ny=0;
	nx=x/c+(x%c==0?0:1);
	ny=x-c*(nx-1);
}
void spfa()
{
	for (int i=1;i<=r*c;i++)
		d[i]=inf,inq[i]=0;
	d[from]=0;
	queue<int> q;
	q.push(from);
	inq[from]=1;
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		inq[x]=0;
		for (int i=h[x];i;i=e[i].ne)
		{
			int y=e[i].y;
			if (d[y]>d[x]+1)
			{
				d[y]=d[x]+1;
				pre[y]=x;
				if (!inq[y]) q.push(y),inq[y]=1;
			}
		}
	}
	cout<<d[to]<<endl;
	int u=to;
	while (pre[u]!=from)
	{
		u=pre[u];
		Get(u);
		a[nx][ny]='.';
	}
	for (int i=1;i<=r;i++)
	{
		for (int j=1;j<=c;j++)
			cout<<a[i][j];
		cout<<endl;
	}
}
void Paint1(int from,int to)
{
	int u=to;
	while (pre[u]!=from)
	{
		u=pre[u];
		Get(u);
		if (a[nx][ny]==' ') a[nx][ny]='.';
		else a[nx][ny]=' ';
	}
}
void Paint2(int from,int to)
{
	int u=to;
	while (pre2[u]!=from)
	{
		u=pre2[u];
		Get(u);
		if (a[nx][ny]==' ') a[nx][ny]='.';
		else a[nx][ny]=' ';
	}
}
int Calc(int from,int to)
{
        for (int i=1;i<=size;i++)
		d2[i]=inf,inq2[i]=0;
	queue<int> q2;
	q2.push(from);
	inq2[from]=1,d2[from]=0;
	while (!q2.empty())
	{
		int x=q2.front();
		q2.pop();
		inq2[x]=0;
		for (int i=h[x];i;i=e[i].ne)
		{
			int y=e[i].y;
			Get(y);
			if (a[nx][ny]=='.'||(a[nx][ny]=='X'&&y!=to)) continue;
			if (d2[y]<=d2[x]+1) continue;
			d2[y]=d2[x]+1;
			pre2[y]=x;
			if (!inq2[y]) q2.push(y),inq2[y]=1;
		}
	}
	return d2[to];
}
void spfa2(int from,int to,int from2,int to2)
{
	for (int i=1;i<=size;i++)
		d[i]=inf,inq[i]=0;
	queue<int> q;
	q.push(from);
	inq[from]=1,d[from]=0;
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		inq[x]=0;
		for (int i=h[x];i;i=e[i].ne)
		{
			int y=e[i].y;
			Get(y);
			if (a[nx][ny]=='X'&&y!=to) continue;
			if (d[y]<=d[x]+1) continue;
			d[y]=d[x]+1;
			pre[y]=x;
			if (!inq[y]) q.push(y),inq[y]=1;
			if (y==to)
			{
				Paint1(from,to);
				int o=Calc(from2,to2);
				if (d[y]+o<ans)
				{
					ans=d[y]+o;
					Paint2(from2,to2);
					for (int j=1;j<=r;j++)
						for (int k=1;k<=c;k++)
							Ans[j][k]=a[j][k];
					Paint2(from2,to2);
				}
				Paint1(from,to);
			}
		}
	}
}
int main()
{
	fx[1][1]=1,fx[1][2]=0,fx[2][1]=-1,fx[2][2]=0;
	fx[3][1]=0,fx[3][2]=1,fx[4][1]=0,fx[4][2]=-1;
        scanf("%d%d",&r,&c);
	size=r*c;
	int cnt=0;
	for (int i=1;i<=r;i++)
	{
		char ch=getchar();
		while (ch!='+'&&ch!='|')
			ch=getchar();
		a[i][1]=ch;
		for (int j=2;j<=c;j++)
		{
			ch=getchar();
			a[i][j]=ch;
			if (a[i][j]=='X') 
			{
				cnt++;
				p[cnt]=C(i,j);
				if (cnt==1) from=C(i,j);
				else to=C(i,j);
			}
		}
	}
	if (cnt==0)
	{
		for (int i=1;i<=r;i++)
		{
			for (int j=1;j<=c;j++)
				cout<<a[i][j];
			cout<<endl;
		}
	}
	Buildgragh();
	if (cnt==2)
	{
		spfa();
	}
	if (cnt==4)
	{
		ans=inf;
		for (int i=1;i<=r;i++)
			for (int j=1;j<=c;j++)
				Ans[i][j]=a[i][j];
		spfa2(p[1],p[2],p[3],p[4]);
		spfa2(p[1],p[3],p[2],p[4]);
		spfa2(p[1],p[4],p[2],p[3]);
		cout<<ans<<endl;
		for (int i=1;i<=r;i++)
		{
			for (int j=1;j<=c;j++)
				cout<<Ans[i][j];
			cout<<endl;
		}
	}
	return 0;
}
</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值