Week8 动态规划+双周赛3

在这里插入图片描述

思路:

此题为完全背包模型,可通过将二维数组转化为一维数组来优化动态规划的算法。

#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];
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值