2019.8.1 JZ DAY1总结

D A Y 1 DAY 1 DAY1

T 1 T1 T1
水 叮 当 的 舞 步 水叮当的舞步

Description
水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~
地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。
水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,地毯左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

Input
每个测试点包含多组数据。
每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。
接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。
N=0代表输入的结束。

Output
对于每组数据,输出一个整数,表示最少步数。
Sample Input
2
0 0
0 0
3
0 1 2
1 1 2
2 2 1
0

Sample Output
0
3

Data Constraint
对于30%的数据,N<=5
对于50%的数据,N<=6
对于70%的数据,N<=7
对于100%的数据,N<=8,每个测试点不多于20组数据。

对于十点半才开始看题的我感觉这套题是很不友好的,因为没有什么比较显然的题目,都是需要深入思考的所以今天题爆零,还能接受吧。
第一题感觉应该可以暴力,但是因为来得晚没时间,索性不交了,如果是纯裸的暴力,理论应该是20+pts的。刚开始的暴力思路是枚举边界点然后看看与边界点相连的有哪些颜色,然后用dfs暴力求答案。正解IDA貌似A也阔以。wot,这两个算法,让以前的我简直感到高不可攀,不过今日学过来,好像只是对暴力的dfs或bfs进行很简单的优化。前者是用迭代加深和估价函数进行优化,后者是直接使用估价函数进行优化,都很简单。迭代加深其实就是枚举一个答案,控制好深度,一旦超过这个深度就不搜了,这样可以较好地控制好这个算法的时间复杂度。然后估价函数还是一个很不错的优化,就是估计当前状态离答案还差多少步,但其实估价函数在大多数时候都只是一个估计的值,并不是准确的值,但依然可以很好地优化dfs或bfs,相当于一种剪枝吧。

#include <cstdio>
using namespace std;

const int fx[4][2] = {{0,1},{1,0},{-1,0},{0,-1}};
const int N = 10;
int v[N][N],n,flag[N][N],ans;
bool vis[10];

int read()
{
	int x = 0,w = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
	return x * w;
}

void dfs(int x,int y,int col)
{
	flag[x][y] = 1;
	for (int i = 0,xx,yy; i <= 3; i ++)
	{
		xx = x + fx[i][0],yy = y + fx[i][1];
		if (xx >= 1 && xx <= n && yy >= 1 && yy <= n && flag[xx][yy] != 1)
		{
			flag[xx][yy] = 2;
			if (col == v[xx][yy]) dfs(xx,yy,col);
		}
	}
}

int gu()
{
	int res = 0;
	for (int i = 0; i <= 5; i ++) vis[i] = 0;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= n; j ++)
			if (flag[i][j] != 1 && !vis[v[i][j]]) res ++,vis[v[i][j]] = 1;
	return res;
}

bool judge(int col)
{
	bool pt = 0;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= n; j ++)
		if (flag[i][j] == 2 && v[i][j] == col) pt = 1,dfs(i,j,col);
	return pt;
}

bool a_star(int dep)
{
	int g = gu();
	if (dep + g > ans) return 0;
	if (!g) return 1;
	int c[N][N];
	for (int i = 0; i <= 5; i ++)
	{
		for (int j = 1; j <= n; j ++)
			for (int k = 1; k <= n; k ++)
				c[j][k] = flag[j][k];
		if (judge(i) && a_star(dep + 1)) return 1; 
		for (int j = 1; j <= n; j ++)
			for (int k = 1; k <= n; k ++)
				flag[j][k] = c[j][k];
	}
	return 0;
}

int main()
{
	while (n = read())
	{
		for (int i = 1; i <= n; i ++)
			for (int j = 1; j <= n; j ++)
				v[i][j] = read(),flag[i][j] = 0;
		dfs(1,1,v[1][1]);
		for (ans = 0; ans <= n * n; ans ++)
			if (a_star(0)) break;
		printf("%d\n",ans);
	}
	return 0;
}

T 2 T2 T2
V a n i 和 C l 2 捉 迷 藏 Vani和Cl2捉迷藏 VaniCl2

Description
vani和cl2在一片树林里捉迷藏……
这片树林里有N座房子,M条有向道路,组成了一张有向无环图。
树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。如果从房子A沿着路走下去能够到达B,那么在A和B里的人是能够相互望见的。
现在cl2要在这N座房子里选择K座作为藏身点,同时vani也专挑cl2作为藏身点的房子进去寻找,为了避免被vani看见,cl2要求这K个藏身点的任意两个之间都没有路径相连。
为了让vani更难找到自己,cl2想知道最多能选出多少个藏身点?

Input
第一行两个整数N,M。
接下来M行每行两个整数x、y,表示一条从x到y的有向道路。

Output
一个整数K,表示最多能选取的藏身点个数。

Sample Input
4 4
1 2
3 2
3 4
4 2

Sample Output
2

Data Constraint
对于20% 的数据,N≤10,M<=20。
对于60% 的数据, N≤100,M<=1000。
对于100% 的数据,N≤200,M<=30000,1<=x,y<=N。

这道题由于题面看起来比较易于理解所以成为了我考场唯一敲的一道题,考场打的是非常暴力的暴力,零分,好呐。正解就是传递闭包,噗,真是xswl,一个弗洛伊德说得那么高深差点看不懂,然后跑一个最大独立集,最大独立集是n - 二分图的最大匹配数。这个虽然不会很严谨的证明,不过还是比较容易感性理解的,因为二分图是一个连通图,那么剩下的一定是不连通的,所以就是答案。

#include <cstdio>
#include <cstring>
using namespace std;

const int N = 210;
const int M = 30010;
int n,m,f[N][N],G[N][N],match[N],ans;
bool vis[N];

int read()
{
	int x = 0,w = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
	return x * w;
}

bool find(int now)
{
	for (int i = 1; i <= G[now][0]; i ++)
	{
		if (vis[G[now][i]]) continue;
		vis[G[now][i]] = 1;
		if (!match[G[now][i]] || find(match[G[now][i]])) {match[G[now][i]] = now; return 1;}
	}
	return 0;
}

int main()
{
	n = read(),m = read();
	for (int i = 1,u,v; i <= m; i ++)
		u = read(),v = read(),f[u][v] = 1;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= n; j ++)
			for (int k = 1; k <= n; k ++)
				if (f[j][i] & f[i][k]) f[j][k] = 1;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= n; j ++)
			if (f[i][j]) G[i][++ G[i][0]] = j;
	for (int i = 1; i <= n; i ++)
	{
		memset(vis,0,sizeof vis);
		if (find(i)) ans ++;
	}
	printf("%d",n - ans);
	return 0;
}

T 3 T3 T3
粉 刷 匠 粉刷匠

Description
赫克托是一个魁梧的粉刷匠,而且非常喜欢思考= =
现在,神庙里有N根排列成一直线的石柱,从1到N标号,长老要求用油漆将这些石柱重新粉刷一遍。赫克托有K桶颜色各不相同的油漆,第i桶油漆恰好可以粉刷Ci根石柱,并且,C1+C2+C3…CK=N(即粉刷N根石柱正好用完所有的油漆)。长老为了刁难赫克托,要求相邻的石柱颜色不能相同。
喜欢思考的赫克托不仅没有立刻开始粉刷,反而开始琢磨一些奇怪的问题,比如,一共有多少种粉刷的方案?
为了让赫克托尽快开始粉刷,请你尽快告诉他答案。

Input
第一行一个正整数T,表示测试数据组数
对于每一组测试数据数据:
第1行:一个正整数K
第2行:K个正整数,表示第i桶油漆可以粉刷的石柱个数,Ci。

Output
对于每组输入数据,输出一行一个整数,表示粉刷的方案数mod 1000000007。
Sample Input
3
3
1 2 3
5
2 2 2 2 2
10
1 1 2 2 3 3 4 4 5 5

Sample Output
10
39480
85937576

Data Constraint
30% N≤10, T≤5
50% N≤15, T≤5
80% K≤15,Ci≤5,T≤500
100% K≤15,Ci≤6,T≤2000
对于这种求方案数的题目我看着就头大,这种dp,我是真的难以下咽,算了也就马马虎虎地把。

#include <cstdio>
#define ll long long
using namespace std;

const int mo = 1e9 + 7;
int T,f[20][100],C[100][100];

int read()
{
	int x = 0,w = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
	return x * w;
}

int min(int a,int b) {return a < b ? a : b;}

int main()
{
	C[0][0] = 1;
	for (int i = 1; i <= 95; i ++)
	{
		C[i][0] = 1;
		for (int j = 1; j <= i; j ++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mo;	
	}
	T = read();
	while (T --)
	{
		int n = read(),sum = 0;
		f[0][0] = 1;
		for (int i = 1,x; i <= n; i ++)
		{
			x = read();
			for (int j = 0; j <= sum + x + 1; j ++) f[i][j] = 0;
			for (int j = 0; j <= sum; j ++)
				for (int k = 0,mi = min(j,x); k <= mi; k ++)
					for (int l = 0,mii = min(sum + 1 - j,x - k); l <= mii; l ++)
					{
						if (k + l == 0) continue;
						int s = f[i - 1][j],y = j + x - k - l;
						s = ((ll)s * (ll)C[x - 1][k + l - 1]) % mo;
						s = ((ll)s * (ll)C[j][k]) % mo;
						s = ((ll)s * (ll)C[sum + 1 - j][l]) % mo;
						f[i][y] = (f[i][y] + s) % mo;
					}
			sum += x;
		}
		printf("%d\n",f[n][0]);
	}
}

额,上一期最后几天有点懒,没有打博客,这期尽量每天都打好吧。
希望这是最后一次爆零。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值