思路:
此题为完全背包模型,可通过将二维数组转化为一维数组来优化动态规划的算法。
#include<iostream>
using namespace std;
int main()
{
int t,m;
cin>>t>>m;
long long w[m+1],v[m+1],f[t+1]={0};
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++)
{
f[j]=max(f[j],f[j-w[i]]+v[i]); //状态转移方程
}
}
cout<<f[t];
return 0;
}
思路:
1、方法:二进制拆分,即把每一个物品根据2的多少次方拆分,因为任何数都可以转化为二进制数。
2、核心思想:把每一个物品拆成很多个,分别计算价值和所需时间,再转化为01背包求解。
3、最后一点:完全背包可以把他的空间记为999999,不要太大,一般百万就足够了。
#include<cstdio>
#include<algorithm>
#define re register int
using namespace std;
int nx,ny,ex,ey,n,f[1010];
int a[10005],b[10005],c[10005];
int tp,co[1000005],v[1000005];//尽可能开大,不要把空间开爆了
inline void pre() {
for(re i=1;i<=n;i++) {
int t=1;
while(c[i]) {
co[++tp]=t*a[i];
v[tp]=t*b[i];
c[i]-=t; t*=2;
if(c[i]<t) {//如果剩下的不能再拆,就直接放在一起
co[++tp]=a[i]*c[i];
v[tp]=b[i]*c[i];
break;
}
}
}
}
int main() {
scanf("%d:%d%d:%d%d",&nx,&ny,&ex,&ey,&n);
int t=(ex*60+ey)-(nx*60+ny);
for(re i=1;i<=n;i++) {
scanf("%d%d%d",&a[i],&b[i],&c[i]);
if(!c[i]) c[i]=999999;
}
pre();//二进制拆分
for(re i=1;i<=tp;i++) {//考虑每个拆出来的物品
for(re j=t;j>=co[i];j--)//01背包板子
f[j]=max(f[j],f[j-co[i]]+v[i]);
}
printf("%d",f[t]);
return 0;
}
思路:
与第一题类似,关键在于找到状态转移方程。当然我的代码还不是最简单的,还能够用滚动数组优化,我就不展示了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=105, mod = 1000007;
int n, m, a[maxn], f[maxn][maxn];
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>a[i];
f[0][0] = 1;
for(int i=1; i<=n; i++)
for(int j=0; j<=m; j++)
for(int k=0; k<=min(j, a[i]); k++)
f[i][j] = (f[i][j] + f[i-1][j-k])%mod;
cout<<f[n][m]<<endl;
return 0;
}
思路:
此题为分组背包模型。
1、分组背包: 将选主件、主件+附件1、主件+附件2、主件+附件1+附件不选5个状态分为一组,这五个状态中只能选一个。这样,就构成了一个分组背包模型。
2、分组背包伪代码:
for 所有的组k
for v=V..0
for 所有的i属于组k
f[v]=max{f[v],f[v-w[i]]+c[i]}
#include<bits/stdc++.h>
using namespace std;
struct node
{
int v;
int w;
};
struct node2
{
int vv,pp,qq,num;
}y[65];
vector<node>h[100];
int dp[32005];
bool cmp(node2 a,node2 b)
{
return a.qq<b.qq;
}
int main()
{
int n,m,i,j,k;
int cnt=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&y[i].vv,&y[i].pp,&y[i].qq);
y[i].num=i;
}
sort(y+1,y+m+1,cmp);
for(i=1;i<=m;i++)
{
node x;
if(y[i].qq==0)
{
x.v=y[i].vv;
x.w=y[i].vv*y[i].pp;
h[y[i].num].push_back(x);
}
else
{
int s=h[y[i].qq].size();
for(j=0;j<s;j++)
{
x.v=y[i].vv+h[y[i].qq][j].v;
x.w=y[i].vv*y[i].pp+h[y[i].qq][j].w;
h[y[i].qq].push_back(x);
}
}
}
for(i=1;i<=m;i++)
{
if(h[i].size()==0) continue;
for(j=n;j>=0;j--)
{
for(k=0;k<h[i].size();k++)
{
if(h[i][k].v<=j) dp[j]=max(dp[j],dp[j-h[i][k].v]+h[i][k].w);
}
}
}
printf("%d\n",dp[n]);
return 0;
}
思路:
题目很基础,我们直接上代码。
#include<iostream>
using namespace std;
int main()
{
string s;
cin>>s;
int len=s.length();
int a[10]={0};
for(int i=0;i<len;i++)
{
if(s[i]=='1'||s[i]=='Q'||s[i]=='A'||s[i]=='Z'||s[i]=='`')
a[1]++;
else if(s[i]=='2'||s[i]=='W'||s[i]=='S'||s[i]=='X')
a[2]++;
else if(s[i]=='3'||s[i]=='E'||s[i]=='D'||s[i]=='C')
a[3]++;
else if(s[i]=='4'||s[i]=='R'||s[i]=='F'||s[i]=='V'||s[i]=='5'||s[i]=='T'||s[i]=='G'||s[i]=='B')
a[4]++;
else if(s[i]=='0'||s[i]=='P'||s[i]==';'||s[i]=='/'||s[i]==']'||s[i]=='['||s[i]=='-'||s[i]=='=')
a[8]++;
else if(s[i]=='9'||s[i]=='O'||s[i]=='L'||s[i]=='.')
a[7]++;
else if(s[i]=='8'||s[i]=='I'||s[i]=='K'||s[i]==',')
a[6]++;
else if(s[i]=='7'||s[i]=='U'||s[i]=='J'||s[i]=='M'||s[i]=='N'||s[i]=='H'||s[i]=='Y'||s[i]=='6')
a[5]++;
else a[8]++;
}
for(int i=1;i<=8;i++)cout<<a[i]<<endl;
return 0;
}
—————————————————————————————————
思路:
题解cv
可以看出,以下切割策略是最佳的。
我们将香肠排列成一条直线,一条接着一条(从而获得由N个较短线段组成的线段)。将这条线切割成M个相等的线段可以得到所需的解。虽然我们在概念上做的是M-1切,但其中一些不是真正的切,而是落在香肠之间(较短的线段)。
例如,对于两条香肠和四个品尝者,第一次切是真实的,将第一条香肠分成两半,第二次切不是真实的,因为它实际上在两条香肠之间,第三次切是真的,将第二条香肠分成一半。
因此,我们可以简单地使用for循环来检查,对于对于每一次切割,它是真切割还是原本中间就是分开的。
另外,有一个明确的公式:解=M-gcd(N,M) (gcd即最大公约数)
个人理解:
我直接利用分式解决,简单明了。
以下是利用分式解决的代码。
#include<iostream>
using namespace std;
int main()
{
int n,m,js=0;
cin>>n>>m;
int x=n,y=m; // x/y
while(x%2==0&&y%2==0)
{
x/=2;
y/=2;
}
const int a=x; //x每次加a
x=0;
while(x/y<n)
{
x+=a;
if(1.0*x/y!=x/y)js++;
}
cout<<js;
return 0;
}
思路:
贪心算法,每结束一轮之后寻找结束时间最早的活动。
#include <bits/stdc++.h>
using namespace std;
int cmp(pair<int, int> p1, pair<int, int> p2)
{
return p1.second < p2.second;
}
int main()
{
int m;
cin>>m;
while(m--){
pair<int, int> p[10010];
int n, res = 0, temp = 0;
cin>>n;
for(int i = 0; i < n; i++){
cin>>p[i].first>>p[i].second;
}
sort(p, p+n, cmp);
for(int i = 0; i < n; i++){
if(temp <= p[i].first){
res++;
temp = p[i].second;
}
}
cout<<res<<endl;
}
}
思路:
利用DFS解答。
#include <bits/stdc++.h>
using namespace std;
string dfs()
{
int n;
string s="",s1;
char x;
while(cin>>x)
{
if(x=='[')
{
cin>>n;
s1=dfs();
while(n--)
{
s=s+s1;
}
}
else
{
if(x==']')
return s;
else
s=s+x;
}
}
}
int main()
{
cout<<dfs();
}
题解:
代码作者:Ethan Hunt丶
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
int len = 1;//表示高精度数据的长度
int sum[100001] = {0,1};//高精度数据计算结果
struct minister{
ll left;
ll right;
} m[1000001];
bool cmp(minister a,minister b){
return a.left * a.right < b.left * b.right;
}
void multiplicative(ll x){//高精度乘法
for(int i = 1;i <= len; i++){
sum[i] *= x; //各位数乘x,如1111×20 = (1000+100+10+1)×20
}
for(int i = 1;i <= len; i++){
sum[i + 1] += sum[i] / 10;//获得进位,因为相乘结果可能为 10 3 13 4,需转化为 1 0 4 3 4
sum[i] %= 10;
}
len++;//因为是sum[i+1],所以肯定会计算到sum[len+1],所以需要len++
while(sum[len] / 10){//观察最高位是否有进位,因为可能有经过前面的操作后可能有 100 3的情况
sum[len + 1] = sum[len] / 10;
sum[len] %= 10;
len++;
}
//经过前面的一波操作后,最后可能会导致sum[len]的位置上有0,若为0则需要消除
if(sum[len] == 0)
len--;
}
void division(){//高精度除法,这里只需要进行一次操作,所以不需要参数
//思路依旧很简单,如2345/5=(2000+300+40+5)/5,小学生除法
//->2 3 4 5 ->0 23(3 + 2%5*10) 4 5 -> 0 4 34(4 + 23%5*10) 5 -> 0 4 6 45(5 + 34%5*10)
for(int i = len; i >= 1; i--){
sum[i - 1] += (sum[i] % m[n].right * 10);
sum[i] /= m[n].right;
}
//这波操作必然导致一堆前导0,需消除
while(!sum[len]){
len--;
}
//防止除完了
if(len == 0) cout << "1" << endl;
}
int main(){
cin >> n;
cin >> m[0].left >> m[0].right;
for(int i = 1;i <= n; i++)
cin >> m[i].left >> m[i].right;
sort(m + 1, m + 1 + n, cmp);
for(int i = 0;i < n; i++){
multiplicative(m[i].left);
}
division();
for(int i = len; i >= 1; i--)
cout << sum[i];
}