TC SRM661 div2

今天本来最初的想法是开 SRM661 div1 的500分题,后来比较悲伤的是,想了一会儿似乎还是没有思路Q=Q。确切的说应该是没有catch到重点才对。同时也因为 SRM661 的题解还没出~ 既然这样的话,我就再来一发div2 练个手什么的好啦~23333

另外最近在空间说《大圣归来》国产动漫危机博同情什么的,又看到了熟悉的论调--“你没看到xxx那么努力么”,《大圣归来》我还没看因此不敢妄加评论,但是对于这种言论我只想说,谁都在努力,凭什么你努力了就应该获得回报?  如果你在那么努力了之后还是差劲的可以,那就请放弃吧,因为不合适。【虽然言辞过激了一点,但是只是想到自己这么多年来的生活,难免对于这种论调深恶痛绝。

然后开始言归正传:~

(一)

题意:给你一张图,图上使用o来表示沙子,用x表示固定的砖块,.表示空格,因为重力作用,沙子会沿着空格往下漏,遇到砖块就不能再往下了,求稳定之后整个图的状态是怎样的,用以下例子来说明:

1.输入状态:                                                          2.最终状态:

ooooo ..o..

..x.. ..x.o

....x ....x

..... .....

....o oo.oo


思路的话 并没有什么特别的,直接模拟就好,但是要注意顺序,对于每一列的话,先判断处于下方的沙子,在判断处于上方的沙子。贴个代码xD

vector <string> simulate(vector <string> board) {
		int n=board.size();
		int m=board[0].size();
		char bo_ma[55][55];
		char re_ma[55][55];
		for (int i=0;i<n;i++)
			for (int j=0;j<m;j++)
				bo_ma[i][j]=board[i][j];
		memset(re_ma,0,sizeof(re_ma));
		for (int col=0;col<m;col++)
			for (int row=n-1;row>=0;row--){
				if (bo_ma[row][col]=='.')
					continue;
				else if (bo_ma[row][col]=='x')
					continue;
				else{
					int bot=row;
					while (bot+1<n && bo_ma[bot+1][col]=='.')
						bot++;
					if (bot!=row){
						bo_ma[bot][col]='o';
						bo_ma[row][col]='.';
					}
				}
			}
		vector <string> ret;
		ret.clear();
		for (int i=0;i<n;i++){
			string tmp="";
			for (int j=0;j<m;j++)
				tmp+=bo_ma[i][j];
			ret.push_back(tmp);
		}
		return (ret);

	}


(二)

题意:已知有2*N个点,分为2行,对于每一行而言都是一条链,给出相邻两点之间的距离。之后需要你在第一条路中选择K个点,使之与第二条路的对应点相连之后构成的无向图,记对于以此法构造出无向图中两点之间距离最远为d,求使得d的最小值可以是多少?

噗,我觉得我题意也没有描述清楚但是看下图应该可以理解题意【他喵的 我最近语文好烂Q=Q


上图是 N=6 K=4 的情况 最终 d=4

然后 我用的方法简直暴力的我自己都不敢直视了Q=Q,我看到N最大值为11,直接通过位运算枚举所有可能的连线方法,然后用floyd暴力求解最长距离,这样的复杂度应该是

O(2^N*N^3)级别的,真是我自己都给吓哭了,但是反正最后就这样跌跌撞撞的过了,也算好吧XD

int gra[55][55];
	int n;
	int fin;
	int floyd() {
		int maxn = -1;
		int f_gra[55][55];
		for (int i=0;i<2*n;i++)
			for (int j=0;j<2*n;j++)
				f_gra[i][j]=gra[i][j];
		for (int k = 0; k < 2 * n; k++)
			for (int i = 0; i < 2 * n; i++)
				for (int j = 0; j < 2 * n; j++)
					if (f_gra[i][k] != -1 && f_gra[k][j] != -1) {
						if (f_gra[i][j] == -1)
							f_gra[i][j] = f_gra[i][k] + f_gra[k][j];
						else
							f_gra[i][j] = min(f_gra[i][j], f_gra[i][k] + f_gra[k][j]);
					}
		for (int i = 0; i < 2 * n; i++)
			for (int j = 0; j < 2 * n; j++)
				maxn = max(maxn, f_gra[i][j]);
		return (maxn);
	}
	int check(int sta) {
		int num = 0;
		for (int i = 0; i < n; i++)
			if ((sta & (1 << i)) != 0)
				num++;
		return (num);
	}
	void modi_map(int sta, int val) {
		for (int i = 0; i < n; i++)
			if ((sta & (1 << i)) != 0)
				gra[i][i + n] = gra[i + n][i] = val;
	}
	int minDiameter(vector<int> a, vector<int> b, int K) {
		n = a.size();
		n++;
		memset(gra, -1, sizeof(gra));
		for (int i = 0; i < 2 * n; i++)
			gra[i][i] = 0;
		for (int i = 0; i < n - 1; i++)
			gra[i][i + 1] = gra[i + 1][i] = a[i];
		for (int i = n; i < 2 * n - 1; i++)
			gra[i][i + 1] = gra[i + 1][i] = b[i - n];
		fin = -1;
		//printf("%d\n",gra[0][1]);
		for (int i = 0; i < (1 << n); i++)
			if (check(i) == K) {
				modi_map(i, 0);
				/*if (i == 27) {
					for (int p = 0; p < 2 * n; p++)
						for (int q = p; q < 2 * n; q++)
							if (gra[p][q] != -1)
								printf("node %d ---> node %d    %d\n", p, q,
										gra[p][q]);
				}*/

				//printf("sta ++++ %d\n",gra[0][1]);
				int tmp = floyd();
				if (fin == -1)
					fin = tmp;
				else
					fin = min(fin, tmp);
				modi_map(i, -1);
			}
		return (fin);
	}


(三)

题意:大概就是你现在有K种颜色可以对N个点进行染色,染色之后对于点i与点j如果有i<j并且他们异色,那么你可以在他们之间连一条边,也可以不连,但是一个点的出度最多只能是1,入度没有限制。下图展示了  N=3  K=2 的情况:



思路:第一眼看上去,这简直是排列组合的题啊,然而做到一半就感觉不对了,包括序号限制,包括出度限制,这限制有点多,如果用排列组合来想的话是很困难的一件事儿。然后就调转枪口,准备开始用dp开始写,dp毕竟是我所有题型中写的比较好的题了吧,首先分析状态,因为K最大值只能取到3,并且N最大值为100,因此就考虑用3维的来做了。因为明显的可以知道,对于某个点而言,他究竟能连出多少条边,能拓展出多少种状态只取决与之前与他不同色的个数有多少,因此状态

f[i][j][k]表示到目前阶段为止,出现颜色1,2,3的个数为i,j,k     可以很明确的知道,如果当前点为p 那么一定有 i+j+k==p   状态确定之后转移也是一气呵成~

f[i+1][j][k]+=f[i][j][k]*(j+k+1) f[i][j+1][k]+=f[i][j][k]*(i+k+1)f[i][j][k+1]+=f[i][j][k]*(i+j+1)

简洁明了~  这三题中我也只对这题感觉自己做的比较满足~2333

然后代码~

<span style="white-space:pre">	</span>long long f_2[105][105];
	long long f_3[105][105][105];
	int countWays(int N, int K) {
		if (K==1)
			return (1);
		else if (K==2){
			memset(f_2,0,sizeof(f_2));
			f_2[0][0]=1;
			for (int st=0;st<N;st++)
				for (int fi=0;fi<=st;fi++){
					int se=st-fi;
					if (f_2[fi][se]!=0){
						f_2[fi+1][se]=f_2[fi+1][se]+f_2[fi][se]*(se+1);
						f_2[fi+1][se]%=MOD;
						f_2[fi][se+1]=f_2[fi][se+1]+f_2[fi][se]*(fi+1);
						f_2[fi][se+1]%=MOD;
					}
				}
			long long ans=0;
			for (int i=0;i<=N;i++){
				ans=ans+f_2[i][N-i];
				ans%=MOD;
			}
			return (ans);
		}
		else if (K==3){
			memset(f_3,0,sizeof(f_3));
			f_3[0][0][0]=1;
			for (int st=0;st<N;st++)
				for (int fi=0;fi<=st;fi++)
					for (int se=0;se+fi<=st;se++){
						int th=st-fi-se;
						if (f_3[fi][se][th]!=0){
							f_3[fi+1][se][th]=f_3[fi+1][se][th]+f_3[fi][se][th]*(se+th+1);
							f_3[fi+1][se][th]%=MOD;
							f_3[fi][se+1][th]=f_3[fi][se+1][th]+f_3[fi][se][th]*(fi+th+1);
							f_3[fi][se+1][th]%=MOD;
							f_3[fi][se][th+1]=f_3[fi][se][th+1]+f_3[fi][se][th]*(fi+se+1);
							f_3[fi][se][th+1]%=MOD;
						}
					}
			long long ans=0;
			for (int i=0;i<=N;i++)
				for (int j=0;j<=N;j++){
					ans=ans+f_3[i][j][N-i-j];
					ans%=MOD;
				}
			return (ans);
		}
	}

嗯 ~如果能每天都刷刷tc的话   真的是很好的一件事儿~  LightOj 最近老是时不时挂掉一下~稍微有点头疼,等到适当的时候一定要准备开div1 了。 今天就差不多这样吧~稍微有点累呢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值