Codeforces Div2 2016.08.29 C题

原创 2016年08月31日 11:09:19

原题目:http://www.codeforces.com/contest/711/problem/C

(翻译如下:)

【题目名称】给树上色

【时限】2s

【空间限制】256M

【题目描述】

有两位好朋友到达了一个公园。公园里生长了n棵树(被编号为1~n)。他们决定将公园里的树涂上颜色。

起初,第i号树拥有初始颜色c[i]。这两位好朋友发现,公园里的树总共只有m种不同的颜色。所以0<=c[i]<=m,当c[i]=0时,意味着这棵树是没有初始颜色的。

两位朋友决定,只给没有初始颜色的树上色(也就是说,只为c[i]=0的树上色)。他们可以从编号为1~m的m种颜色中,选取一种来给树涂色。为第i棵树涂上第j种颜色需要花费代价p[i][j].

他们定义了树的美丽值:

将1~n号树,分成若干段,要求每一段的树颜色都相同。这样分得的段数的最小值,就是树的美丽值。例如,当树的颜色依次为:2,1,1,1,3,2,2,3,1,3时,树的美丽值是7,因为我们可以将这10棵树最少分为7段:{2},{1,1,1},{3},{2,2},{3},{1},{3}.

他们想给所有没有初始颜色的树涂上颜色,使得这些树的美丽值恰好为k.你的程序需要帮助他们求出达成目标的最小涂色代价。当然,也有可能无法达成目标。

注意,他们不能给有初始颜色的树上色。

【输入格式】

第一行,三个整数n,m,k.(1<=k<=n<=100,1<=m<=100).

第二行,n个整数c[1],c[2],…,c[n],(0<=c[i]<=m),表示树的初始颜色。当c[i]=0时,表示i号树无初始颜色,当c[i]不为0时,表示第i号树的初始颜色为c[i].

第3~n+2行,每行m个正整数,其中第i+2行的第j个数表示给第i号树涂上第j种颜色的代价p[i][j].拥有初始颜色的树,也会有相应的涂色代价,不过尽管如此,你仍然不能为它们涂色。(1<=p[i][j]<=10^9).

【输出格式】

一个整数.如果能够达成目标,输出最小涂色代价;如果不能达成目标,输出-1.

Examples
input
3 2 2
0 0 0
1 2
3 4
5 6
output
10
input
3 2 2
2 1 2
1 3
2 4
3 5
output
-1
input
3 2 2
2 0 0
1 3
2 4
3 5
output
5
input
3 2 3
2 1 2
1 3
2 4
3 5
output
0
【样例说明】参见原题目.

【题解】

这是一道动态规划题。通常我们可以定下状态f[i][j],表示使前i棵树的美丽值为j需要的最小代价。不过我们发现这个状态并不方便转移,它根本没有体现颜色带来的影响。

于是我们决定多加一维,记录第i号树的颜色。于是用f[i][j][p]表示使得前i棵树美丽值为j,且第i棵树颜色恰好为p的最小涂色代价。

现在写转移方程。

如果第i棵树有初始颜色,那么没有办法,我们必须使用它原本的颜色。于是转移方程为:

f[i][j][p]=minn{minn{f[i-1][j-1][q]},f[i-1][j][p]}.(1<=q<=m,q!=p).

什么意思呢?首先我们可以与第i-1棵树不同色,这样美丽值会+1,或者我们也可以与第i-1棵树同色,这样美丽值不变。

如果第i棵树没有初始颜色,我们就需要枚举1~m的所有颜色,作为i号树的颜色,状态转移方程与上面类同,只是多一次循环。

现在来考虑效率,枚举i,j已有2层循环,如果树有初始颜色,那么只需再枚举最小值,共3层循环;如果树没有初始颜色,我们需要枚举结尾颜色,同时还要枚举对应的最小值,共4层循环。时间复杂度O(n*k*m^2).n,k,m<=100,

于是运算量是10^8级别的。好在常数不大,Codeforces的评测机跑得飞快,于是108ms就过了。

【参考代码1】

#include<cstdio>
#define ll long long 
#define inf 0x7ffffffffffffff
int n,m,k;
ll price[110][110],f[110][110][110];
int color[110];
ll minn(ll a,ll b)
{
	return a<b?a:b;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=k;j++)
		{
			for(int t=1;t<=m;t++)
			{
				f[i][j][t]=inf;
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&color[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
		 	scanf("%I64d",&price[i][j]);
		}
	}
	ll Minn;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i&&j<=k;j++)
		{
			Minn=inf;
			if(color[i]!=0)
			{
				for(int p=1;p<=m;p++)
				{
					if(p!=color[i])
					{
						Minn=minn(Minn,f[i-1][j-1][p]);
					}
				}
				f[i][j][color[i]]=minn(Minn,f[i-1][j][color[i]]);
			}
			else
			{
				for(int t=1;t<=m;t++)
				{
					Minn=inf;
					for(int p=1;p<=m;p++)
					{
						if(p!=t)
						{
							Minn=minn(Minn,f[i-1][j-1][p]);
						}
					}
					f[i][j][t]=minn(Minn,f[i-1][j][t])+price[i][t];
				}
			}
		}
	}
	bool flag=0;
	ll ans=inf;
	for(int i=1;i<=m;i++)
	{
		if(f[n][k][i]<100000000000)
		{
			flag=1;
			ans=minn(ans,f[n][k][i]);
		}
	}
	if(flag)
	{
		printf("%I64d",ans);
	}
	else
	{
		printf("-1");
	}
	return 0;
}

———————————————————————————————————————————————————

不过我们还是有必要考虑一下优化。对于最小值,我们是否可以预处理,以避免每次都枚举一遍。我们可以记下一个Minn[i][j][t]数组,它表示f[i][j][p](1<=p<=m,p!=t)的最小值。然而你会发现上当了,因为有了这个数组,我们的确不用去枚举一遍找最小值,但是维护这个数组,有需要多出一层循环,时间复杂度没有变化,反而增大了常数。

———————————————————————————————————————————————————

我们只用Minn[i][j]表示f[i][j][p](1<=p<=m)的最小值是否可行?你会发现,这无法解决同色与异色的问题。于是我们想到,可以记录最小值与次小值(可以等于最小值)。如果最小值与当前颜色的值不等,我们直接选用它即可,因为这可以保证一定不同色。如果最小值与当前颜色的值相等,我们取次小值。而每次更新也是O(1)的复杂度。总时间复杂度是O(n*k*m),10^6级别。不过好像这样写常数增大了不少,在Codeforces上跑了一下,31ms过。比裸的暴力动规还是要快不少。但没有想象的那么快,也许是我代码写得太丑了。

【参考代码2】

#include<cstdio>
#define ll long long 
#define inf 0x7ffffffffffffff
int n,m,k;
ll price[110][110],f[110][110][110],MINN[110][110][2];
int color[110];
ll minn(ll a,ll b)
{
	return a<b?a:b;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=k;j++)
		{
			for(int t=1;t<=m;t++)
			{
				f[i][j][t]=inf;
			}
			MINN[i][j][0]=MINN[i][j][1]=inf;
		}
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&color[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
		 	scanf("%I64d",&price[i][j]);
		}
	}
	ll Minn;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i&&j<=k;j++)
		{
			Minn=inf;
			if(color[i]!=0)
			{
				if(MINN[i-1][j-1][0]!=f[i-1][j-1][color[i]])
				{
					Minn=MINN[i-1][j-1][0];
				}
				else
				{
					Minn=MINN[i-1][j-1][1];
				}
				f[i][j][color[i]]=minn(Minn,f[i-1][j][color[i]]);
				if(f[i][j][color[i]]<=MINN[i][j][0])
				{
					MINN[i][j][1]=MINN[i][j][0];
					MINN[i][j][0]=f[i][j][color[i]];
				}
				else if(f[i][j][color[i]]<MINN[i][j][1])
				{
					MINN[i][j][1]=f[i][j][color[i]];
				}
			}
			else
			{
				for(int t=1;t<=m;t++)
				{
					if(MINN[i-1][j-1][0]!=f[i-1][j-1][t])
					{
						Minn=MINN[i-1][j-1][0];
					}
					else
					{
						Minn=MINN[i-1][j-1][1];
					}
					f[i][j][t]=minn(Minn,f[i-1][j][t])+price[i][t];
					if(f[i][j][t]<=MINN[i][j][0])
					{
						MINN[i][j][1]=MINN[i][j][0];
						MINN[i][j][0]=f[i][j][t];
					}
					else if(f[i][j][t]<MINN[i][j][1])
					{
						MINN[i][j][1]=f[i][j][t];
					}
				}
			}
		}
	}
	bool flag=0;
	ll ans=inf;
	for(int i=1;i<=m;i++)
	{
		if(f[n][k][i]<100000000000)
		{
			flag=1;
			ans=minn(ans,f[n][k][i]);
		}
	}
	if(flag)
	{
		printf("%I64d",ans);
	}
	else
	{
		printf("-1");
	}
	return 0;
}

# When Who Problem Lang Verdict Time Memory
20293256 2016-08-31 06:30:12 19991202lym C - Coloring Trees GNU C++11 Accepted 31 ms 10500 KB

 
 
 
 
My contest submissions
 
 
# When Who Problem Lang Verdict Time Memory
               
20250771 2016-08-29 16:36:50 19991202lym C - Coloring Trees GNU C++11 Accepted 108 ms 10300 KB

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Codeforces Div.2 2016.08.29 B题(翻译)

【题目名称】幻方 【题目描述】 有一个n*n的方阵,其中有且仅有一个位置是空的,其余位置都填有正整数。现在需要在空位处填入一个正整数,使得该方阵每一行的数之和,每一列的数之和,以及主、副对角线上的数之...

Codeforces Round #382 (Div. 2)A到D题 A. Ostap and //B. Urbanization//C. Tennis Championship//D. Taxes

A. Ostap and Grasshopper time limit per test 2 seconds memory limit per test 256 megabytes inp...

Codeforces Aim Tech Round 3 (Div.2 )C.Letters Cyclic Shift 【贪心】水题

C. Letters Cyclic Shift time limit per test 1 second memory limit per test 256 megabytes ...

Codeforces Round #182 (Div. 2)---C. Yaroslav and Sequence(不错的题,分析找规律)

Yaroslav has an array, consisting of (2·n - 1) integers. In a single operation Yaroslav can change t...

Codeforces Round #385 (Div. 2)C. Hongcow Builds A Nation【并查集+贪心】好题~

C. Hongcow Builds A Nation time limit per test 2 seconds memory limit per test 256 megab...

Codeforces Round #353 (Div. 2) C. Money Transfers 环、贪心、前缀和推广、好题

首先最多是ans = n-1次,这个时候只有sum总是0; 否则, 只有 有一个小区间是 0, 则ans就少1次; 所以要找到,尽可能多的sum == 0 的区间。 当时一直不知道,环该怎么处理, 结...

codeforces round # 273 div2 A, B, C题题解

A. Initial Bet time limit per test 1 second memory limit per test 256 megabytes input st...

Codeforces #319(Div.2) C. Vasya and Petya's Game(数学题)

题意:A给出一个数x,B每次猜一个y,A回答B,x是否可以被y整除,求出要猜的最小次数和需要猜的数。 素数筛处理出所有素数,枚举每个素数p,可以知道如果p^k<n,则p^k一定需要选,根据这个原则求出...

codeforces #262 DIV2 C题Present(二分+贪心)

题目地址:http://codeforces.com/contest/460/problem/C 这个题是用二分枚举最小值,然后判断能否在规定的次数内使得所有的数都达到这个值。判断的时候要用贪心的方法...

【Codeforces Round 375 (Div 2) C】【语文题】Polycarp at the Radio

C. Polycarp at the Radio time limit per test 2 seconds memory limit per test 256 megabyt...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)