一、考试感想
我也不想说什么了...10分orz...
测评的时候第一次全部“文件错误”0分(".in"和".out"没打),尴尬了十多分钟,第二次测评时
最有把握的“morning”用的DFS-穷竭大法居然一个点都没有QAQ....
最后不知道是哪道题蒙对了一个点,光荣10分....(四个同胞跟我一起“光荣”了233)
ps.还好没0分哦,不然真的要“哭死”了...总之这次考试就是个锻炼吧,毕竟几乎所有要用的算法都不会。
BUT——老师说如果“搜索”打得好的话,就算是四道题全部暴搜都有起码150分诶!!!
确实不应该,实力差距蛮大的。
顺便提一下,老师说,现阶段吧“搜索”和“DP”掌握好就基本“小无敌”了....qwq....
/俩都没掌握好的我...qwq...
二、题目
(一)晨练计划 'morning'
【题目描述】
奶牛们打算通过锻炼来培养自己的运动细胞,作为其中的一员,贝茜选择的 运动方式是每天进行N(1 <= N <= 10,000)分钟的晨跑。在每分钟的开始,贝茜会选择下一分钟是用来跑步还是休息。 贝茜的体力限制了她跑步的距离。
更具体地,如果贝茜选择在第i分钟内跑 步,她可以在这一分钟内跑D_i(1 <= D_i <= 1,000)米,并且她的疲劳度会增加 1。不过,无论何时贝茜的疲劳度都不能超过M(1 <= M <= 500)。如果贝茜选择 休息,那么她的疲劳度就会每分钟减少1,但她必须休息到疲劳度恢复到0为止。 在疲劳度为0时休息的话,疲劳度不会再变动。晨跑开始时,贝茜的疲劳度为0。 还有,在N分钟的锻炼结束时,贝茜的疲劳度也必须恢复到0,否则她将没有 足够的精力来对付这一整天中剩下的事情。
请你计算一下,贝茜最多能跑多少米。
【输入格式】
第1行: 2个用空格隔开的整数:N 和 M
第2..N+1行: 第i+1为1个整数:D_i
【输出格式】
第1行: 输出1个整数,表示在满足所有限制条件的情况下,贝茜能跑的最大距离
Sample Input
5 2
5
3
4
2
10
Sample Output
9
输出说明:
贝茜在第1分钟内选择跑步(跑了5米),在第2分钟内休息,在第3分钟内跑步(跑了4米),剩余的时间都用来休息。因为在晨跑结束时贝茜的疲劳度必须为0,所以她不能在第5分钟内选择跑步。
【分析】
①俗称:水题dp(考试时用的暴搜:全部运行错误炸掉orz)
②设d[i][j]为i分钟疲劳为j(d存的值为距离)
d[i][j]=d[i-1][j-1]+a[i]——当前距离为上一分钟走的距离+当前分钟可走的距离
d[i][0]=max(d[i-1][0], d[i-j][j])——当前疲劳值为0的两种情况:
1.上一分钟已经为0 2.一直休息至此疲劳值降为0
[看题解很简单,自己想就炸掉]
【代码】(看了题解后自己码的)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=10005,M=505;
int f[N][M],n,m,d[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&d[i]);
for(int i=1;i<=n;i++)
{
f[i][0]=max(f[i][0],f[i-1][0]);
for(int j=1;j<=m;j++)
{
f[i][j]=f[i-1][j-1]+d[i];
if(i-j>0)
f[i][0]=max(f[i][0],f[i-j][j]);
}
}
printf("%d\n",f[n][0]);
return 0;
}
(二)方程的解 'solution'
【题目描述】
【输入格式】
第1行:1个整数N (1 ≤ N ≤ 100),表示元素的个数
按下来N行,每行一个整数,数的大小在-30000 ~ 30000之间,数值均不相同
【输出格式】
第1行:1个整数,表示满足条件的6个数的组数。
【样例】
【分析】
①等式:‘等号左右两边相等’ —>双向搜索
②鉴于个人能力有限,所以有种“暴力方法”:枚举
③枚举也要讲技巧啊——等式左右两边分开枚举,即:六层循环降至两个三层循环+统计答案
④这种方法也“能力有限”,100分的点最多拿到70分,还是好好学好算法吧qwq
【代码】(70分'map枚举法'——好像还可以用set,研究中...)
#include<cstdio>
#include<map>
using namespace std;
map <int, long long> cnt;
int n,ans=0;
int a[100+5];
int main()
{
//freopen("solution.in", "r", stdin);
//freopen("solution.out", "w", stdout);
//(a*b+c)/d-e=f
//a*b+c=(f+e)*d
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
//等式左边
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
if(a[k])//d!=0(0不能为除数)
cnt[a[i]*a[j]+a[k]]++;
//等式右边
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
ans+=cnt[(a[i]+a[j])*a[k]];
printf("%d\n",ans);
return 0;
}
【代码】(标程_(:з」∠)_)
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100
typedef long long LL;
int n,a[MAXN+10],b[MAXN*MAXN*MAXN+10],c[MAXN*MAXN*MAXN+10];
int cntb,cntc,cnt[MAXN*MAXN*MAXN+10];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
b[++cntb]=a[i]*a[j]+a[k];
sort(b+1,b+cntb+1);
for(int i=1;i<=cntb;i++)
{
c[++cntc]=b[i];
cnt[cntc]=1;
while(i+1<=cntb&&b[i+1]==b[i])
cnt[cntc]++,i++;
}
LL ans=0;
for(int i=1;i<=n;i++)
{
if(!a[i]) continue;
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
int pos=lower_bound(c+1,c+cntc+1,a[i]*(a[j]+a[k]))-c;
if(c[pos]==a[i]*(a[j]+a[k]))
ans+=cnt[pos];
}
}
printf("%lld\n",ans);
return 0;
}
(三)移动玩具 'movetoy'
【题目描述】
在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移动到某人心中的目标状态。
【输入格式】
前4行表示玩具的初始状态,每行4个数字1或0,1表示方格中放置了玩具,0表示没有放置玩具。接着是一个空行。接下来4行表示玩具的目标状态,每行4个数字1或0,意义同上。
【输出格式】
一个整数,所需要的最少移动次数。
Sample Input
1111
0000
1110
0010
1010
0101
1010
0101
Sample Output
4
【分析】
个人感觉类似“八数码”,都是把当前状态进行操作变成目标状态/一本正经的胡说八道qwq...
反正目前这道题“不管自己什么事”/是你太弱了好吗qwq!
【代码】(悄咪咪的附上标程/反正看不到我看不到我)
为什么附上来之后格式那么丑...
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int go[4][2]= {{-1,0},{0,1},{1,0},{0,-1}};
struct rec
{
int map[5][5];
};
struct node
{
rec x;
int y;
node(rec xx,int yy):x(xx),y(yy) {}
};
LL hash(rec x)
{
LL h=0;
for (int i=1; i<=4; i++)
for (int j=1; j<=4; j++)
h^=(x.map[i][j]<<(4*(i-1)+(j-1)));
return h;
}
int main()
{
queue<node>que;
set<LL>bt;
rec start,finish;
for (int i=1; i<=4; i++)
{
char c[10];
scanf("%s",&c);
for (int j=0; j<4; j++)
start.map[i][j+1]=(c[j]=='1')?1:0;
}
for (int i=1; i<=4; i++)
{
char c[10];
scanf("%s",&c);
for (int j=0; j<4; j++)
finish.map[i][j+1]=(c[j]=='1')?1:0;
}
if (hash(start)==hash(finish))
{
printf("0\n");
return 0;
}
que.push(node(start,0));
bt.insert(hash(start));
while (!que.empty())
{
node cur=que.front();
que.pop();
for (int i=1; i<=4; i++)
for (int j=1; j<=4; j++)
if (cur.x.map[i][j])
for (int k=0; k<4; k++)
{
int x=i+go[k][0],y=j+go[k][1];
if ((!x)||(x>4)||(!y)||(y>4)) continue;
if (cur.x.map[x][y]) continue;
rec next=cur.x;
next.map[x][y]=next.map[i][j]--;
if (bt.count(hash(next))) continue;
que.push(node(next,cur.y+1));
bt.insert(hash(next));
if (hash(finish)==hash(next))
{
printf("%d\n",cur.y+1);
return 0;
}
}
}
return 0;
}
(四)关灯 'lightsout'
【题目描述】
奶牛们喜欢在黑暗中睡觉。每天晚上,他们的牲口棚有L(3<=L<=50)盏灯,他们想让亮着的灯尽可能的少。他们知道按钮开关的位置,但喜闻乐见的是他们并没有手指。你得到了一个长度为T(1<=T<=7)的插槽用以帮助奶牛们改变灯的状态。
【输入格式】
第一行,两个整数L和T。
第二行给出一个长度为L的01串表示初始灯的状态,0表示灯是灭的,1表示灯是亮的。第三行给出一个长度为T的01串,表示你获得的插槽。
【输出格式】
第一行输出一个整数K,表示在满足亮着的灯最少的情况下,你要用插槽操作的次数。
第二行到第K+1行,每行一个整数表示你的插槽使用的位置。
"K最小的解,并且满足解的字典序最大(即按钮开关的位置尽可能靠后)"
Sample Input
10 4
1111111111
1101
Sample Output
5
1
3
4
6
7
样例说明
使用5次插槽
1111111111 初始状态
0010111111 对第一个位置使用插槽
0001101111 对第三个位置使用插槽
0000000111 对第四个位置使用插槽
0000011101 对第六个位置使用插槽
0000010000 对第七个位置使用插槽
可以证明这是满足字典序最小的最优解。
【分析】
一句话——迭代加深搜索!
其实只是不想说多了,容易暴露自己的弱_(:з」∠)_
能力范围之内的AB题本人已经说得尽可能的清楚了,求放过
【代码】(来来来,既然不会就先存代码qwq...)
#include <bits/stdc++.h>
using namespace std;
int i,n,m,ID;
char S[200],T[200];
int s[200],t[200],d[200],stk[200],num;
int dfs(int tt,int now,int light)
{
int tmp=0;
if(light>ID)return 0;
if(now>num)return 0;
if(tt>n-m+1)
{
for(int i=tt; i<=n; i++)
if(s[i])light++;
if(light>ID)
return 0;
if(now>num)
return 0;
for(int i=1; i<=now; i++)
stk[i]=d[i];
num=now;
return 1;
}
for(int i=tt; i<=tt+m-1; i++)
s[i]^=t[i-tt+1];
d[now+1]=tt;
tmp=dfs(tt+1,now+1,light+s[tt])||tmp;
d[now+1]=0;
for(int i=tt; i<=tt+m-1; i++)
s[i]^=t[i-tt+1];
tmp=dfs(tt+1,now,light+s[tt])||tmp;
return tmp;
}
int main()
{
cin>>n>>m;
num=777;
scanf("%s",S+1);
scanf("%s",T+1);
for(i=1; i<=n; i++)
s[i]=S[i]-48;
for(i=1; i<=m; i++)
t[i]=T[i]-48;
for(ID=0; ID<=n; ID++)
if(dfs(1,0,0))
break;
printf("%d\n",num);
for(i=1; i<=num; i++)
printf("%d\n",stk[i]);
return 0;
}