P1616 疯狂的采药
题目描述
LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是 LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:
11. 每种草药可以无限制地疯狂采摘。
22. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
输入格式
输入第一行有两个整数,分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m。
第 2 到第 (m+1) 行,每行两个整数,第 (i+1) 行的整数 ai,bi 分别表示采摘第 i 种草药的时间和该草药的价值。
输出格式
输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
由于每种药材可以无限次采集,所以这是一道完全背包的题目
完全背包可以使用1维数组来构建状态转移方程
此时背包的遍历是从w[i]开始
因为dp[j]=max(dp[j],dp[j-w[i]]+v[i])
由于一开始j=w[i],所以dp[j]会被初始化为dp[j-w[i]]+v[i],所以一个数据会被多次加入
dp[1] = dp[1-w[0]] + v[0]
dp[2] = dp[2-w[0]] + v[0]
达成了药材可以无限采集的条件
完整代码
#include<bits/stdc++.h>
using namespace std;
long long t,m;
long long w[10005],v[10005];
long long dp[10000005];
int main()
{
cin>>t>>m;
for(int i=1;i<=m;i++)
{
cin>>w[i]>>v[i];
}
for(int i=1;i<=m;i++)
{
for(int j=w[i];j<=t;j++)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
cout<<dp[t];
return 0;
}
P3367 【模板】并查集
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N,M ,表示共有N 个元素和 M 个操作。
接下来 M 行,每行包含三个整数 Zi,Xi,Yi 。
当 Zi=1 时,将 Xi 与 Yi 所在的集合合并。
当 Zi=2 时,输出 Xi 与 Yi 是否在同一集合内,是的输出 Y
;否则输出 N
。
输出格式
对于每一个Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
这道是并查集的模板题,要写这道题就要学习并查集的算法
并查集是一种树状的数据结构,用于解决不相交集合的合并和查询问题
主要操作有三个
- 初始化:将每个点所在集合初始化为它本身
- 查找,查找元素所在的集合
- 合并:按所需把对应两个元素所在集合合并一起
初始化函数
void iint()
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
}
查找函数
int find(int u)
{
if(fa[u]==u)
{
return u;
}
else
{
return fa[u]=find(fa[u]);
}
}
合并函数
关于合并函数,还需要在合并前判断其是否都在同一个集合里
如果两个元素都在同一个集合里
有f[a]==f[b]
void join(int u,int v)
{
u=find(u);
v=find(v);
if(v==u) return;
fa[v]=u;
}
bool judge(int u,int v)
{
u=find(u);
v=find(v);
return u==v;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
int n;
int fa[10005];
void iint()
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
}
int find(int u)
{
if(fa[u]==u)
{
return u;
}
else
{
return fa[u]=find(fa[u]);
}
}
void join(int u,int v)
{
u=find(u);
v=find(v);
if(v==u) return;
fa[v]=u;
}
bool judge(int u,int v)
{
u=find(u);
v=find(v);
return u==v;
}
int main()
{
int m,z,x,y;
cin>>n>>m;
iint();
for(int i=1;i<=m;i++)
{
cin>>z>>x>>y;
if(z==1)
{
join(x,y);
}
if(z==2)
{
if(judge(x,y))
{
cout<<"Y"<<endl;
}
else
{
cout<<"N"<<endl;
}
}
}
return 0;
}
Spreadsheets
题意翻译
人们常用的电子表格软件(比如: Excel)采用如下所述的坐标系统:
第一列被标为 A,第二列为 B,以此类推,第 2626 列为 Z。接下来为由两个字母构成的列号: 第 2727 列为 AA,第 2828 列为 AB ⋯⋯ 在标为 ZZ 的列之后则由三个字母构成列号,如此类推。
行号为从 11 开始的整数。
单元格的坐标由列号和行号连接而成。比如,BC23 表示位于第 5555 列 2323 行的单元格。
有时也会采用被称为 RXCY 的坐标系统,其中 X 与 Y 为整数,坐标 (X,Y) 直接描述了对应单元格的位置。比如,R23C55 即为前面所述的单元格。
您的任务是编写一个程序,将所给的单元格坐标转换为另一种坐标系统下面的形式。
输入
第一行一个整数 n (1≤n≤10^5) 表示将会输入的坐标的数量。
接下来 n 行,每行一个坐标。
注意: 每个坐标都是正确的。此外不会出现行号或列号大于 106106 的单元格。
输出 n 行,每行一个被转换的坐标。
输出
n 行,每行一个被转换的坐标。
从1对应A到26对应Z
可以把它看作一个26进制
因此我们要做的是解决26进制和10进制之间的转换
用数余26得的数对应26个字母
对于大于26的字符
这里用一个while循环来处理
每次除26来获得后一位的字符转换
但是当余数为0时对应的是Z,要特判
完整代码
#include<bits/stdc++.h>
using namespace std;
int n,lg;
string s;
void transform(bool mode)//参数为判断函数
{
char tr[]=" ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int lh=0,hh=0;//列号和行号
if(mode)//第一种形式RXCY转EXcel
{
int r=s.find("R");
int c=s.find("C");
for(int i=r+1;i<c;i++)
{
hh=hh*10+s[i]-'0';//行不需要转换
}
for(int i=c+1;i<lg;i++)
{
lh=lh*10+s[i]-'0';//把列转成数字好转换
}
string ans;//定义字符串
while(lh>0)
{
int temp=lh%26;
if(temp==0)//特判余数为0,此时为Z
{
temp=26;
lh-=26;
}
ans+=tr[temp];//用temp余数去对应字母,将转换字符加入字符串
lh/=26;//获得后一位进行转换
}
reverse(ans.begin(),ans.end());//因为ans加入字符是反着加入,从低到高,因此需要反转
cout<<ans<<hh<<endl;//输出
}
else//EXCel转RXCY
{
for(int i=0;i<lg;i++)
{
if(isdigit(s[i]))//读到数字
{
hh=hh*10+s[i]-'0';//行不用转换
}
else
{
lh=lh*26+s[i]-'A'+1;//将列转换
}
}
cout<<"R"<<hh<<"C"<<lh<<endl;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s;
bool mode=0,flag=0;
lg=s.length();
for(int i=0;i<lg;i++)
{
if(isdigit(s[i]))//读到数字
{
flag=1;
}
if(flag&&s[i]=='C')//EXcel的C前面不含数字,RXCY的C前面必含数字
{
mode=1;
break;
}
}
transform(mode);
}
return 0;
}