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