补题地址
update:
7.21 :t3添加转移函数 t4完善思路,修改代码小bug
7.23 :添加t5题解链接
7.24 :添加t6题解链接
为节省篇幅,前三题的代码我放到了github上
Problem Description
我们有 nn 种不同的饮料,每种饮料有无限多瓶,第 ii 种饮料一瓶提供 x[i]x[i] 毫升的水分,包含 y[i]y[i] 卡路里。现在我们需要选择一种饮料一直喝,直到补充了至少 mm 毫升的水分,我们想使得摄入的卡路里总和最小。请求出这个最小值。
一旦打开一瓶饮料,就一定要喝完。
Input第一行一个整数 test(1 ≤ \le ≤ test ≤ \le ≤ 100)表示数据组数。
对于每组数据,第一行两个整数 n,m(1≤n≤100,1≤m≤10000)。
接下来 nn 行,每行两个整数 x[i], y[i](1 ≤ \le ≤x[i], y[i] ≤ \le ≤ 100)x[i],y[i])。
注意只能选择一种饮料喝!(所以不是完全背包)
饮料种类不多,遍历就可以了
代码:https://github.com/dq116/-/blob/master/Drink.cpp
Problem Description
小沃沃一共参加了 4 门考试,每门考试满分 100 分,最低 0 分,分数是整数。
给定四门考试的总分,请问在最优情况下,四门课绩点的和最高是多少?分数与绩点之间的对应关系如下:
95~100 4.3
90~94 4.0
85~89 3.7
80~84 3.3
75~79 3.0
70~74 2.7
67~69 2.3
65~66 2.0
62~64 1.7
60~61 1.00~59 0
Input
第一行一个正整数 test(1≤test≤401) 表示数据组数。 接下来 test 行,每行一个正整数 x 表示四门考试的总分
(0≤x≤400)。
典型的多重背包问题
给个区间就是糊弄人嘞,绩点到手要那么高分就没用了,直接取区间下限就行。
当然数据量较小,直接枚举就行。
代码:https://github.com/dq116/-/blob/master/GPA.cpp
(我直接套的这篇博客的多重背包的板子)
Problem Description
初始有 a,b 两个正整数,每次可以从中选一个大于 1 的数减 1,最后两个都会减到 1,我们想知道在过程中两个数互质的次数最多是多少。Input
第一行一个正整数 test(1≤test≤1000000) 表示数据组数。接下来 test 行,每行两个正整数 a,b(1≤a,b≤1000)
dp O(n^2)预处理,O(1)查询
用 f[i][j]表示第一个数字从 i开始减,第二个数字从 jj开始减的情况下最多有多少对互质的数字。
f[i][j]=max(f[i-1][j],f[i][j-1])+t
其中当i与j互质时t=1否则t=0;
(输入输出不要用cin,cout!会TLE)
(别试着找数学规律了,我试过了,没啥规律)
代码:https://github.com/dq116/-/blob/master/Dec.cpp
Problem Description
这是一个回合制游戏,每一回合开始前会进行上一回合的结算。有一张 n∗n 的棋盘,我们出生在一个初始位置 (x,y),现在我们要选择一个位置建设城市。
你的人物每回合可以移动到距离你曼哈顿距离不超过 2 的位置,移动完成后可以选择是否建立城市。
建立城市后,你的人物消失,成为一个人口为 1 的城市,这个人口要下回合才可以工作。如果不移动,直接在 (x,y) 建城,第 1回合就可以开始工作。
对于城市的每个居民,你可以安排他到距离城市曼哈顿距离小于等于 3
的位置进行工作,此居民可以瞬间到达该位置,每个位置最多安排一个居民,失业的人口不会生产任何食物。注意,城市位置上必须有一个居民在工作。
结算按照如下顺序:
- 如果位置 (i,j) 上有一个工作居民,则获得 a[i][j] 的食物。
- 如果当前城市人口为 i,且食物达到 8∗ i 2 i^2 i2 时,你获得一个新的居民,下一回合可以进行操作。
当结算后城市总人口达到 9 游戏结束。
初始食物数量为 0,人口上涨不会导致之前积累的食物消失。输出最少几个回合能让游戏结束。
Input
第一行一个正整数 test(1≤test≤10) 表示数据组数。对于每组数据,第一行三个正整数 n(2≤n≤500),x(1≤x≤n),y(1≤y≤n),分别表示棋盘大小和起始位置。
接下来 n 行,每行 n 个整数,第 i 行第 j 列的整数表示 a[i][j] (1≤a[i][j]≤3)。
注意在开始建城之前我们可以移动多次!
地图太小,没地方给人工作就有人失业了。
建好城后我们优先让人去食物最多的地方工作(我用的优先队列)
在哪建城我们可以遍历一下可能的地点(把那由近及远的一层层的可能的地点想像成一颗树,初始位置为树根,每下一层代表建城前多移动一次可能到的地点,根到节点的距离代表移动次数)
(遍历图的话理论上用dfs的也可以,得多加个判断,处理不当就得不到最短路了。bfs的遍历过程,就相当于一层层地遍历树,自然路径也是最短的)
代码(注释挺详细的)
#include
#include
#include
using namespace std;
const int n_max=500,population_max=9,a3_num=24,a2_num=12;
int test;
int n,x,y;
int g[n_max+1][n_max+1];
int a3[a3_num],b3[a3_num],a2[a2_num],b2[a2_num];//移动曼哈顿距离3和2的移动方案
void init()//计算移动方案
{
int step=3;
int counter=0;
for(int i=-step;i<=step;i++)
{
for(int j=-step;j<=step;j++)
{
if(abs(i)+abs(j)<=step && (i!=0 || j!=0)) //排除不移动:i==0 && j==0的情况
{a3[counter]=i;
b3[counter]=j;
//cout<
counter++;
}
}
}
//cout<
step=2;
counter=0;
for(int i=-step;i<=step;i++)
{
for(int j=-step;j<=step;j++)
{
if(abs(i)+abs(j)<=step && (i!=0 || j!=0))
{
a2[counter]=i;
b2[counter]=j;
//cout<
counter++;
}
}
}
//cout<
}
int get_round(int x,int y)//城市在x,y处完成游戏所对应的回合数
{
int p=1;//现在的人数
int produce[10]={0,g[x][y]};//每个人对应的每回合可以获取的食物数
int round=0;//回合数
priority_queue<int> q;//让食物多的地方率先被分配
for(int i=0;i<a3_num;i++)
{
int n_x=x+a3[i];
int n_y=y+b3[i];
if(n_x<=n && n_x>0 &&n_y <=n && n_y>0 )
{
q.push(g[n_x][n_y]);
}
}
int total=0;//积累的总食物
while(p<population_max)
{
for(int i=1;i<10;i++)
{
total+=produce[i];
}
if(total>=p*p*8)
{
p++;
if(!q.empty())//空则表明有人失业了,这个人不能产出食物了
{
produce[p]=q.top();
q.pop();
}
}
round++;
}
return round;
}
int mini_round; //最小回合数
int visited[n_max+1][n_max+1];
int bfs()
{
mini_round=get_round(x,y);
memset(visited,0,sizeof(visited));
int n_x,n_y;
visited[x][y]=1;
queue<pair<int,int>> q;
int level=0;
q.push(make_pair(x,y));
int rounds[mini_round]={1};//记录每层的合法的点数
while (!q.empty())
{
pair<int,int> temp=q.front();
mini_round=min(get_round(temp.first,temp.second)+level,mini_round);
q.pop();
for(int i=0;i<a2_num;i++)
{
n_x=temp.first+a2[i];
n_y=temp.second+b2[i];
if(n_x<=n && n_x>0 &&n_y <=n && n_y>0 && !visited[n_x][n_y])
{
//cout<
q.push(make_pair(n_x,n_y));
visited[n_x][n_y]=1;
rounds[level+1]+=1;//压的是下一层的点所以level+1
}
}
rounds[level]-=1;
if( rounds[level]==0) //该层遍历完了该遍历下一层了
{
level++;
if(level>=mini_round) /*每下一层就在建城前多移动了level回合
如果移动的回合大于目前最小的总回合数,那再往放下一层找,也不可能产生更优解了*/
break;
}
}
return mini_round;
}
int main()
{
init();
cin>>test;
for(int k=0; k<test; k++)
{
scanf("%d%d%d",&n,&x,&y);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&g[i][j]);
}
}
cout<<bfs()<<endl;
}
}
十分抱歉
第一版发布前把memset(visited,0,sizeof(visited));
改成了int visited[n_max][n_max]={0}导致地址越界了;
由于visited数组少开了一行,之前在静态区还没事,放到栈区后,访问到边界时,就越界了。
T5 (图文详解)
T6 (解释的不是很详细但思路挺不一样的)
T7-T8没看