2021/2/17 考试总结

 

T1 音量调节

题目描述

一个吉他手准备参加一场演出。他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都要改变一次音量。在演出开始之前,他已经做好了一个列表,里面写着在每首歌开始之前他想要改变的音量是多少。每一次改变音量,他可以选择调高也可以调低。

音量用一个整数描述。输入文件中给定整数beginLevel,代表吉他刚开始的音量,以及整数maxLevel,代表吉他的最大音量。音量不能小于0也不能大于maxLevel。输入文件中还给定了n个整数c1,c2,…,cn,表示在第i首歌开始之前吉他手想要改变的音量是多少。

吉他手想以最大的音量演奏最后一首歌,你的任务是找到这个最大音量是多少。

输入格式

第一行依次为三个整数:n, beginLevel, maxlevel。

第二行依次为n个整数:c1,c2,…,cn。

输出格式

输出演奏最后一首歌的最大音量。如果吉他手无法避免音量低于0或者高于maxLevel,输出−1。

样例数据

input1

3 5 10
5 3 7

output1

10

input2

4 8 20
15 2 9 10

output2

-1

数据规模与约定

1≤n≤50,1≤ci≤maxLevel,1≤maxLevel≤1000,0≤beginLevel≤maxLevel。

思路

设 f[i][j] 表示当前是第 i 首歌,能否将音量调节为 j

代码如下

#include<bits/stdc++.h>
using namespace std;
bool f[60][1200];
int n,Begin,Max,c[120];
int main()
{
	freopen("ChangingSounds.in","r",stdin);
	freopen("ChangingSounds.out","w",stdout);
	scanf("%d%d%d",&n,&Begin,&Max);
	memset(f,0,sizeof(f));
	f[0][Begin]=1;
	for(int i=1;i<=n;i++)
	scanf("%d",&c[i]);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=Max;j++)
		{
			if(f[i-1][j])
			{
				if(j+c[i]<=Max) f[i][j+c[i]]=1;
				if(j-c[i]>=0) f[i][j-c[i]]=1;
			} 
		}
	}
	int ans=-1;
	for(int j=0;j<=Max;j++)
	{
		if(f[n][j])
		ans=max(ans,j);
	}
	cout<<ans;
	return 0;
} 

总结

直接AC

T2 配对

题目描述

有 N 个编号 1,2,⋯,N 的男人和有 N 个编号 1,2,⋯,N 的女人。

对于每个 i,j(1≤i,j≤N),男人 i 和女人 j 的匹配度是一个整数 ai,j 。当 ai,j=1 时,男人 i 和女人 j 可以匹配。当 ai,j=0 时,两人不匹配。

芋头正在尝试配出N对一男一女的配对。 其中,每个男人和每个女人都必须且只能属于某一个配对。

请计算出芋头配出N对的方案数对 1e9+7 取模的结果。

输入格式

第一行一个数字 N 。

接下来 N 行, 第i行有N 个空格隔开的数 ai,1 到 ai,N 。

N

a1,1 ⋯⋯ a1,N

⋮⋮

aN,1 ⋯⋯ aN,N

输出格式

输出芋头配出N对的方案数对1e9+7 取模的结果。

样例

输入样例1

3
0 1 1
1 0 1
1 1 1

输出样例1

3

样例解释1

有三种配对的方法如下( (i,j) 表示男人 i 与女人 j 配对):

  • (1,2),(2,1),(3,3)
  • (1,2),(2,3),(3,1)
  • (1,3),(2,1),(3,2)

输入样例2

4
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0

输出样例2

1

样例解释2

有一种配对的方法如下:

  • (1,2),(2,4),(3,1),(4,3)

输入样例3

1
0

输出样例3

0

输入样例4

21
0 0 0 0 0 0 0 1 1 0 1 1 1 1 0 0 0 1 0 0 1
1 1 1 0 0 1 0 0 0 1 0 0 0 0 1 1 1 0 1 1 0
0 0 1 1 1 1 0 1 1 0 0 1 0 0 1 1 0 0 0 1 1
0 1 1 0 1 1 0 1 0 1 0 0 1 0 0 0 0 0 1 1 0
1 1 0 0 1 0 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0
0 1 1 0 1 1 1 0 1 1 1 0 0 0 1 1 1 1 0 0 1
0 1 0 0 0 1 0 1 0 0 0 1 1 1 0 0 1 1 0 1 0
0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1
0 0 1 0 0 1 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1
0 0 0 0 1 1 0 0 1 1 1 0 0 0 0 1 1 0 0 0 1
0 1 1 0 1 1 0 0 1 1 0 0 0 1 1 1 1 0 1 1 0
0 0 1 0 0 1 1 1 1 0 1 1 0 1 1 1 0 0 0 0 1
0 1 1 0 0 1 1 1 1 0 0 0 1 0 1 1 0 1 0 1 1
1 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 1 1 0 0 1
0 0 0 1 1 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1
1 0 1 1 0 1 0 1 0 0 1 0 0 1 1 0 1 0 1 1 0
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 0 1
0 0 0 1 0 0 1 1 0 1 0 1 0 1 1 0 0 1 1 0 1
0 0 0 0 1 1 1 0 1 0 1 1 1 0 1 1 0 0 1 1 0
1 1 0 1 1 0 0 1 1 0 1 1 0 1 1 1 1 1 0 1 0
1 0 0 1 1 0 1 1 1 1 1 0 1 0 1 1 0 0 0 0 0

输出样例4

102515160

数据范围与提示

所有输入的值都是整数。

1≤N≤21

ai,j 是 0 或 1 。

思路

看到数据范围,直接状压dp

设 f[i][j] 为匹配到第 i 个男人 ,女人 的匹配状态 为 j 的方案数

则可以循环枚举 j 的 每一个 1,判断 这个1 代表的女人和第i个男人是否匹配

状态转移方程 f[i][j]+=f[i-1][k];

因为第i个人的状态 j 必定有 i 个 1,所以可以提前预处理

加个滚动数组优化

#include<bits/stdc++.h>
using namespace std;
int n,a[25][25];
long long f[2][1<<21];
vector<int> S[22];
int lowbit(int x)
{
	return x&-x;
}
int main()
{
	freopen("pair.in","r",stdin);
	freopen("pair.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a[i][j]);
		}
	}
	for(int i=1;i<1<<n;i++) 
	{
		int cnt=0;
		for(int j=i;j;j-=lowbit(j))
		cnt++;
		S[cnt].push_back(i);
	}
	f[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int t=0;t<S[i].size();t++)
		{
			int j=S[i][t];
			for(int k=1;k<=n;k++)
			{
				if(((j>>(k-1))&1)&&a[i][n-k+1]==1)
				{
					f[i&1][j]+=f[(i-1)&1][j-(1<<(k-1))];
					if(f[i&1][j]>1e9+7)
					f[i&1][j]-=(1e9+7); 
				}
			}
		}
	} 
	cout<<f[n&1][(1<<n)-1];
	return 0;
} 

总结

直接AC

T3 补兵

题面

对于一个DOTA玩家,补兵个数(Score)是衡量一名选手能力的重要指标,特别是打路人局的时候,补兵能力就更加关键了,因为常常会有队友和你抢补刀,比如,队友操控的老鹿在开大收兵,如果你操控的是幽鬼,就需要在老鹿的AOE中偷偷补上几刀来保证自己的发育。

我们自己建立一个模型来大致模拟以下情况:

现在有n个小兵,每个小兵有自己的血量Ai(血量一定是正整数),你和老鹿轮流对小兵进行攻击。每次,你可以选择对某个小兵造成1点伤害(或者你可以选择不作为),接着,老鹿会对所有小兵造成1点AOE伤害,如此往复,直到所有小兵都死亡(血量变成0)。如果你对某个小兵造成致命伤害(使他的血量从1变成0).那么你就补刀成功了!

对于给定的情形,你需要计算你最多可以补刀多少的小兵。

输入格式

本题有多组测试数据,第一行为一个整数TT,表示测试组数。 对于每一组数据,第一行一个整数NN,表示小兵的个数,随后第二行NN个正整数,表示每个小兵的血量。

输出格式

对于每一组数据,输出一个整数M,表示补兵的最大数量。

数据规模

对于30%的数据,N≤100。 对于50%的数据,N≤500,T≤30,Ai≤500。 对于100%的数据,1≤N≤500,1≤T≤70,1≤Ai≤1000。

Sample 1

sample input

1
5
5 5 5 5 5

sample output

2

思路

稍后再补

总结

考试时不会,0分

T4 蜘蛛棋盘

题目描述

在一个 n×m的棋盘上,第一天每一个格子上都有一个蜘蛛。

在第一天里每一只蜘蛛都可以 向上 或 向下 或 向左 或 向右 爬行 一格(不能走出棋盘); 或者待在原地不动。 多只蜘蛛可以爬行到同一个格子中。

通过第一天爬行后,第二天棋盘上出现了一些空的(没有蜘蛛的)格子。问,第二天棋盘上最多可以空出多少个格子?

输入格式

一行,两个正整n,m 。

输出格式

一行,一个非负整数,表示最多能空出的格子数。

样例

输入样例1

1 1

输出样例1

0

输入样例2

2 3

输出样例2

4

数据范围与提示

对于 20% 的数据,n=1。

对于 50%的数据,n≤3。

对于 100% 的数据,1≤n,m≤40,1≤n⋅m≤40 。

思路

一个比较奇怪的思路

枚举第一行的只蜘蛛分别往哪个格子爬,或者说那些格子收集蜘蛛

也就是2^m种状态

再往下一依次循环,如果 第i-1行,第 j 列 格子的蜘蛛未被收集

就用这个格子把他收集

也就是 第 i 行 消 第 i-1 行

到了第 n 行时把剩余的格子的蜘蛛收集就好了

ps:考试时发现有的n,m互换之后,答案变了!

 于是就在跑过n,m之后,把m,n也算了一遍,取一个最优值

#include<bits/stdc++.h>
using namespace std;
int n,m;
bool f[40][40];
int com(int l)
{
	int cnt=99999999;
	if(l%3==0) cnt=(l/3);
	if(l%3==1) cnt=(l/3+1);
	if(l%3==2) cnt=(l/3+1);
	if(l%2==0) cnt=min((l/2),cnt);
	return cnt;
}
int Com()
{
	int ans=99999999;
	int cnt=0;
	for(int s=0;s<1<<m;s++)
	{
		cnt=0;
		memset(f,0,sizeof(f));
		for(int i=1;i<=m;i++)
		{
			if((s>>(i-1))&1)
			{
				f[1][i]=1;
				f[1][i-1]=1;
				f[1][i+1]=1;
				f[2][i]=1;
				cnt++;			
			}
		}
		for(int i=2;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(!f[i-1][j])
				{
					f[i-1][j]=1;
					f[i][j]=1;
					f[i][j-1]=1;
					f[i][j+1]=1;
					f[i+1][j]=1;
					cnt++;
				}
			}
		}
		int len=0;
		for(int i=1;i<=m;i++)
		{
			if(!f[n][i])
			{
				len++;
			}
			else if(len!=0)
			{
				cnt+=com(len);
				len=0;
			}
		}
		if(len!=0) cnt+=com(len); 
		ans=min(cnt,ans);
	}
	return ans;
 } 
int main()
{
	freopen("spiders.in","r",stdin);
	freopen("spiders.out","w",stdout);
	scanf("%d%d",&n,&m);
	if(n==1||m==1)
	{
		if(n>m) swap(n,m);
		cout<<m-com(m);
		return 0;
	}
	else 
	{
		int temp1=Com();
		swap(n,m);
		int temp2=Com();
		cout<<n*m-min(temp1,temp2);
	}
	return 0;
} 

总结

考试时也想到了状压,但是实在不会,考试快结束时又想打表缩短时间,但还是没来得及

直接AC

T5  [usaco2010mar_gold]奶牛大聚会

题目描述

Bessie 正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。

每个奶牛居住在 N 个农场中的一个,这些农场由N−1 条道路连接,并且从任意一个农场都能够到达另外一个农场。道路 i 连接农场 Ai 和 Bi,长度为Li。集会可以在 N 个农场中的任意一个举行。另外,每个牛棚中居住着 Ci 只奶牛。

在选择集会的地点的时候,Bessie 希望最大化方便的程度(也就是最小化不方便程度)。比如选择第 X 个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和(比如,农场 ii到达农场 X 的距离是 20,那么总路程就是 Ci×20)。帮助 Bessie 找出最方便的地点来举行大集会。

输入格式

第一行一个整数 N 。

第二到 N+1 行:第 i+1 行有一个整数 Ci。

第 N+2 行到 2N 行:第i+N+1 行为 3 个整数:Ai,Bi 和 Li。

输出格式

一行一个整数,表示最小的不方便值。

样例

输入样例

5 
1 
1 
0 
0 
2 
1 3 1 
2 3 2 
3 4 3 
4 5 3

输出样例

15

数据范围与提示

1≤N≤1e5,1≤Ai≤Bi≤N,0≤Ci,Li≤1e3。

思路

基础换根dp

代码

#include<bits/stdc++.h>
using namespace std;
const long long maxn = 1e5+7;
struct node
{
	long long y,v,next;
}e[2*maxn];
long long siz[maxn],fa[maxn],c[maxn],link[maxn],t,id=1,sum=0;
long long dis[maxn];
void Insert(long long x,long long y,long long v)
{
	e[++t].y=y;
	e[t].v=v;
	e[t].next=link[x];
	link[x]=t;
}
long long n;
void dfs1(long long x,long long pre,long long v)
{
	sum+=c[x]; 
	fa[x]=pre;
	siz[x]=c[x];
	for(long long i=link[x];i;i=e[i].next)
	{
		long long y=e[i].y;
		if(y==pre) continue;
		dfs1(y,x,e[i].v);
		siz[x]+=siz[y];
	}
	dis[1]+=siz[x]*v;
}
void dfs2(long long x)
{
	for(long long i=link[x];i;i=e[i].next)
	{
		long long y=e[i].y;
		if(y==fa[x]) continue;
		dis[y]=dis[x]+1ll*(sum-siz[y]-siz[y])*e[i].v; 
		dfs2(y);
	 } 
	 if(dis[x]<dis[id])
	 id=x;
} 
int main()
{
	freopen("gather.in","r",stdin);
	freopen("gather.out","w",stdout);
	scanf("%d",&n);
	for(long long i=1;i<=n;i++)
	scanf("%d",&c[i]);
	for(long long i=1;i<n;i++)
	{
		long long x,y,v;
		scanf("%d%d%d",&x,&y,&v);
		Insert(x,y,v);
		Insert(y,x,v);
	}
	dfs1(1,0,0);
	id=1;
	dfs2(1);
	cout<<dis[id];
	return 0;
}

总结

考试时只把dis改成long long 了

结果中间溢出了,只有70分



总的来说该拿的分也拿到了

除了没开long long有点难受

期望得分:100+100+0+80+100=380

实际得分:100+100+0+100+70=370

猜你喜欢

转载自blog.csdn.net/jwg2732/article/details/113833726