ACM周记(四)

(一)动态规划背包问题

问题 C: 混合背包

题目描述

一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn。有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

输入

第一行:二个整数,V(背包容量,V<=200),N(物品数量,N<=30);

第2..N+1行:每行三个整数Wi,Ci,Pi,前两个整数分别表示每个物品的重量,价值,第三个整数若为0,则说明此物品可以购买无数件,若为其他数字,则为此物品可购买的最多件数(Pi)。

输出

仅一行,一个数,表示最大总价值。

样例输入 

10 3
2  1  0
3  3  1
4  5  4

样例输出 

11

 思路:pi为0时,用完全背包,其他情况用多重背包

#include<bits/stdc++.h>
using namespace std;
const int N = 300;
int f[N], v[N], w[N],s[N];
int main()
{
    int n, m;
    cin >> m >> n;
    for (int i = 1; i <= n;i++){
        cin >> v[i] >> w[i] >> s[i];
        
    }

    for (int i = 1; i <= n; i++){
        if(s[i] == 0){
            for (int j = v[i]; j<=m;j++){
                f[j] = max(f[j], f[j - v[i]] + w[i]);    //完全背包
            }
        }
        else{
            for (int j = 1; j <= s[i];j++){
                for (int k = m; k >=v[i];k--){
                    f[k] = max(f[k], f[k - v[i]] + w[i]);  //多重背包
                }
            }
        }
    }
    cout << f[m];
}

问题 D: 潜水员

题目描述

潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?

例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119

如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。

你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

输入

第一行有2整数m,n(1<=m<=21,1<=n<=79)。它们表示氧,氮各自需要的量。

第二行为整数k(1<=n<=1000)表示气缸的个数。

此后的k行,每行包括ai,bi,ci(1<=ai<=21,1<=bi<=79,1<=ci<=800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。

输出

仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。

样例输入

5 5
9
1 1 12
3 1 52
1 3 71
2 1 33
3 2 86
2 3 91
2 2 43
3 3 113
1 2 28

样例输出 

104

 题意:满足氮气和氧气的需求下求最少重量

思路:双重费用求最小值

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int f[N][N], v[N], w[N], v2[N];
int main()
{
    int n, m, u;
    cin >> u >> m>>n;
    memset(f, 0x3f3f3f, sizeof(f));   //求最小值所以初始化为足够大的数
    f[0][0] = 0;            //第一个数初始化为0
    for (int i = 1; i <= n; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        for (int i = u; i>= 0;i--){       //费用1
            for (int j = m; j >= 0;j--){        //费用2
                f[i][j] = min(f[i][j], f[max(0, i - x)][max(0, j - y)] + z);
            }
        }
    }
        cout << f[u][m];
}

问题 M: 逃亡总动员

题目描述

在《Harry Potter and the Deathly Hallows》中,Harry Potter他们一起逃亡,现在有许多的东西要放到赫敏的包里面,但是包的大小有限,所以我们只能够在里面放入非常重要的物品,现在给出该种物品的数量、体积、价值的数值,希望你能够算出怎样能使背包的价值最大的组合方式,并且输出这个数值,赫敏会非常地感谢你。

输入

第1行有2个整数,物品种数n和背包装载体积v。 
第2行到n+1行每行3个整数,为第i种物品的数量m、体积w、价值s。

输出

仅包含一个整数,即为能拿到的最大的物品价值总和。

样例输入 

2 10 
3 4 3 
2 2 5

样例输出 

13

思路:完全背包,但是要用二进制优化

#include <bits/stdc++.h>
using namespace std;
const int N = 100000;
int f[N], v[N], w[N], s[N];
int main()
{
    int n, m, x, y, z, cnt = 0;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> z >> x >> y;
        //二进制优化,拆分为01背包
        int k = 1;
        while (k <= z)
        {
            cnt++;
            v[cnt] = k * x;
            w[cnt] = k * y;
            z -= k;
            k *= 2;
        }
        if (z > 0)
        {
            cnt++;
            v[cnt] = z * x;
            w[cnt] = z * y;
        }
    }
    n = cnt;    //背包容量要更新
    for (int i = 1; i <= n; i++)
    {
        for (int j = m; j >= v[i]; j--)
        {
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    cout << f[m];
}

问题 N: 暗黑game

题目描述

暗黑游戏中,装备直接决定玩家人物的能力。可以使用Pg和Rune购买需要的物品。暗黑市场中的装备,每件有不同的价格(Pg和Rune)、能力值、最大可购买件数。Kid作为暗黑战网的一个玩家,当然希望使用尽可能少的Pg和Rune购买更优的装备,以获得最高的能力值。请你帮忙计算出现有支付能力下的最大可以获得的能力值。 

输入

第1行,三个整数N,P,R,分别代表市场中物品种类,Pg的支付能力和Rune的支付能力。 
 第2..N+1行,每行四个整数,前两个整数分别为购买此物品需要花费的Pg,Rune,第三个整数若为0,则说明此物品可以购买无数件,若为其他数字,则为此物品可购买的最多件数(S),第四个整数为该装备的能力值。

输出

仅一行,一个整数,最大可获得的能力值。

样例输入 

3 10 10 
5 3 0 110
4 3 4 120 
2 3 1 130 

样例输出 

370

思路:双重费用加完全多重混合背包

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int  f[N][N], v[N], w[N], v2[N],s[N];
int main()
{
    int n, m, u,cnt=0;
    memset(f, 0, sizeof(f));
    cin >> n >> u >> m;
    for (int i = 1; i <= n; i++)
    {
        int x, y, z, t;
        cin >>x>>y>>z>>t;
        if(z==0)   //因为背包容量是有限的,所以能把完全背包转化为多重背包
            z = min(u / x, m / y);
        for (int i = z; i>0; i--)
        {
            for (int j = u; j >= x; j--)   //费用1
            {
                for (int k = m; k >= y; k--)  //费用2
                {
                    f[j][k] = max(f[j][k], f[j - x][k - y] + t);
                }
            }
        }
    }
    cout << f[u][m] << endl;
}

(二)数论

B - A sequence of numbers

Xinlv wrote some sequences on the paper a long time ago, they might be arithmetic or geometric sequences. The numbers are not very clear now, and only the first three numbers of each sequence are recognizable. Xinlv wants to know some numbers in these sequences, and he needs your help.

Input

The first line contains an integer N, indicting that there are N sequences. Each of the following N lines contain four integers. The first three indicating the first three numbers of the sequence, and the last one is K, indicating that we want to know the K-th numbers of the sequence.

You can assume 0 < K <= 10^9, and the other three numbers are in the range [0, 2^63). All the numbers of the sequences are integers. And the sequences are non-decreasing.

Output

Output one line for each test case, that is, the K-th number module (%) 200907.

Sample

InputcopyOutputcopy
2 1 2 3 5 1 2 4 5 
5 16 

 题意:根据给定序列(等比或等差)前3项,输出序列第k个数字

思路:判断序列是不是等差数列,是就用等差数列公式计算,不是就用快速幂算等比数列

#include <iostream>
using namespace std;

typedef long long ll;
const int c = 200907;

ll f(ll a,ll b){      //快速幂
    a %= c;
    ll t = 1;
    while(b){
        if(b&1)
            t = t * a % c;
        a = a * a % c;
        b >>= 1;
    }
    return t % c;
}
int main()
{
    int n;
    cin >> n;
    while(n--){
        ll x, y,z;
        int k;
        cin >> x >> y >> z >> k;
        int ans;
        if(y-x==z-y){          //等比数列
            ll d = y - x; 
            ll a0 = x - d;
            ans = (a0 % c + d % c * k) % c;
        }
        else{                    //等差数列
            ll q = z / y;
            ll a0 = x;
            ans = a0 % c * f(q, k - 1) % c;
        }
        cout << ans << endl;
    }
    return 0;
}

D - Fibonacci

In the Fibonacci integer sequence, F0 = 0, F1 = 1, and Fn = Fn − 1 + Fn − 2 for n ≥ 2. For example, the first ten terms of the Fibonacci sequence are:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …

Givn an integer n, your goal is to compute the last 4 digits of Fn.

Input

The input test file will contain multiple test cases. Each test case consists of a single line containing n (where 0 ≤ n ≤ 1,000,000,000). The end-of-file is denoted by a single line containing the number −1.

Output

For each test case, print the last four digits of Fn. If the last four digits of Fn are all zeros, print ‘0’; otherwise, omit any leading zeros (i.e., print Fn mod 10000).

Sample

InputcopyOutputcopy
0
9
999999999
1000000000
-1
0
34
626
6875

题意:输出斐波那锲数列第n项最后四位数字

思路:因为要取模运算,所以用高精度模拟不方便,直接矩阵快速幂

#include <iostream>
using namespace std;

const int N = 2;
typedef long long ll;

int n;
ll mod = 10000;

void mul(int c[], int a[], int b[][N])
{
    int t[N] = {0};
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            t[i] = (t[i] + a[j] * b[j][i]%mod)%mod;
    memcpy(c, t, sizeof t);
}
void mul(int c[][N], int a[][N], int b[][N])
{
    int t[N][N] = {0};
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            for (int k = 0; k < N; k++)
                t[i][j] = (t[i][j] + a[i][k] * b[k][j]%mod)%mod;
    memcpy(c, t, sizeof t);
}
void solve()
{
    int f[N] = {0, 1};
    int a[N][N] =
        {
            {0, 1},
            {1, 1},
        };
    while (n)
    {
        if (n & 1)
            mul(f, f, a); // f = f * a;
        mul(a, a, a);     // a = a * a;
        n >>= 1;
    }
    cout << f[0] << endl;
}
int main(void)
{
    while (cin>>n,n!=-1)
        solve();
    return 0;
}

 B - 寻找素数对

哥德巴赫猜想大家都知道一点吧.我们现在不是想证明这个结论,而是想在程序语言内部能够表示的数集中,任意取出一个偶数,来寻找两个素数,使得其和等于该偶数.
做好了这件实事,就能说明这个猜想是成立的.
由于可以有不同的素数对来表示同一个偶数,所以专门要求所寻找的素数对是两个值最相近的.

Input

输入中是一些偶整数M(5<M<=10000).

Output

对于每个偶数,输出两个彼此最接近的素数,其和等于该偶数.

Sample

InputcopyOutputcopy
20 30 40 
7 13 
13 17 
17 23 

题意:输入一个偶数m,寻找两个距离最近且相加等于m的素数(两个数可以相同)

思路:从n/2的位置开始筛素数,两个素数相加等于m就结束

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 10010;
int prime[N];
bool st[N];
int n,cnt=0;

void getprime(){                //线性筛
    for (int i = 2; i <= n;i++){
        if(!st[i])
            prime[cnt++]=i;
        for (int j = 0; prime[j] <= n / i;j++){
            st[prime[j] * i] = true;
            if(i%prime[j]==0)
                break;
        }
    }
}
int main()
{
    while(~scanf("%d",&n)){
        cnt = 0;
        memset(st, false, sizeof(st));
        getprime();
        int a = n / 2;
        for (int i = a; i > 0;i--){
            if(st[i]==false&&st[n-i]==false){
                cout << i << " " << n - i << endl;
                break;
            }
        }
    }
    return 0;
}

 C - Max Factor

To improve the organization of his farm, Farmer John labels each of his N (1 <= N <= 5,000) cows with a distinct serial number in the range 1..20,000. Unfortunately, he is unaware that the cows interpret some serial numbers as better than others. In particular, a cow whose serial number has the highest prime factor enjoys the highest social standing among all the other cows.

(Recall that a prime number is just a number that has no divisors except for 1 and itself. The number 7 is prime while the number 6, being divisible by 2 and 3, is not).

Given a set of N (1 <= N <= 5,000) serial numbers in the range 1..20,000, determine the one that has the largest prime factor.

Input

* Line 1: A single integer, N

* Lines 2..N+1: The serial numbers to be tested, one per line

Output

* Line 1: The integer with the largest prime factor. If there are more than one, output the one that appears earliest in the input file.

Sample

InputcopyOutputcopy
4 36 38 40 42 
38

 题意:找出t个数中素数因子最大的那个

#include <iostream>
#include <algorithm>
#include <cmath>
#include<cstring>
using namespace std;

int prime[30000];
int a[10000];

void divide(int x)
{
    int t = x;
    for (int i = 2; i <= x / i; i++) // i <= x / i:防止越界,速度大于 i < sqrt(x)
        if (x % i == 0)              // i为底数
        {
            while (x % i == 0)
                x /= i;
            prime[t] = i;
        }
    if (x > 1)
        prime[t] = x; //如果x还有剩余,单独处理
}

int main()
{
    int n;
    while (cin >> n)
    {
        memset(prime, 0, sizeof(prime));
        for (int i = 0; i < n; i++)
        {
            cin >> a[i];
            divide(a[i]);
        }
        int max = -1, maxindex = 0;
        for (int i = 0; i < n; i++)
        {
            if (max < prime[a[i]])
            {
                max = prime[a[i]];
                maxindex = a[i];
            }
        }
        cout << maxindex << endl;
    }
    return 0;
}

D - Twin Prime Conjecture

 

If we define dn as: dn = pn+1-pn, where pi is the i-th prime. It is easy to see that d1 = 1 and dn=even for n>1. Twin Prime Conjecture states that "There are infinite consecutive primes differing by 2".
Now given any positive integer N (< 10^5), you are supposed to count the number of twin primes which are no greater than N.

Input

Your program must read test cases from standard input.
The input file consists of several test cases. Each case occupies a line which contains one integer N. The input is finished by a negative N.

Output

For each test case, your program must output to standard output. Print in one line the number of twin primes which are no greater than N.

Sample

InputcopyOutputcopy
1 5 20 -2
0 1 4

题意:输入n,计算1-n中孪生素数(两两之间相差2)的个数

思路:先筛素数,然后找合法的素数

#include <iostream>
#include <cstring>
#include<cmath>
using namespace std;
const int N = 1e5;
const int sqr = sqrt(N);

int num[N+1];
bool st[N];
int n, cnt = 0;

void f(){
    st[0] = true, st[1] = true;

    for (int i = 2; i <= sqr; i++)
    {
        if (!st[i])
        for (int j = i*i; j <= N; j+=i)
        {
            st[j] = true;
        }
    }
    num[0] = num[1] = num[2] = num[3] = num[4] = 0;
    for (int i = 2; i <= N;i++)
        if (!st[i - 2] && !st[i])
            num[i] = num[i - 1] + 1;
        else
            num[i] = num[i - 1];
}

int main()
{
    f();
    while (scanf("%d", &n) != EOF && n >= 0)
    {
        memset(st, false, sizeof(st));
        printf("%d\n", num[n]);
    }
    return 0;
}

 Ignatius and the Princess III

"Well, it seems the first problem is too easy. I will let you know how foolish you are later." feng5166 says.

"The second problem is, given an positive integer N, we define an equation like this:
  N=a[1]+a[2]+a[3]+...+a[m];
  a[i]>0,1<=m<=N;
My question is how many different equations you can find for a given N.
For example, assume N is 4, we can find:
  4 = 4;
  4 = 3 + 1;
  4 = 2 + 2;
  4 = 2 + 1 + 1;
  4 = 1 + 1 + 1 + 1;
so the result is 5 when N is 4. Note that "4 = 3 + 1" and "4 = 1 + 3" is the same in this problem. Now, you do it!"

Input

The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file.

Output

For each test case, you have to output a line contains an integer P which indicate the different equations you have found.

Sample

InputcopyOutputcopy
4 10 20 
5 42 627

母函数公式

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1000;
int a[N + 5], b[N + 5];

void solve()
{
	for (int i = 0; i <= N;i++){
		a[i] = 1;
	}
	for (int k = 2; k <= N;k++){
		for (int i = 0; i <= N;i++){
			for (int j = 0; j + i <= N;j+=k){
				b[i + j] += a[i];
			}
		}
		for (int i = 0; i <= N;i++){
			a[i] = b[i];
			b[i] = 0;
		}
	}
}

int main()
{
	int n, m;
	solve();
	while(cin>>n){
		cout << a[n] << endl;
	}
	return 0;
}

A - 小兔的棋盘

小兔的叔叔从外面旅游回来给她带来了一个礼物,小兔高兴地跑回自己的房间,拆开一看是一个棋盘,小兔有所失望。不过没过几天发现了棋盘的好玩之处。从起点(0,0)走到终点(n,n)的最短路径数是C(2n,n),现在小兔又想如果不穿越对角线(但可接触对角线上的格点),这样的路径数有多少?小兔想了很长时间都没想出来,现在想请你帮助小兔解决这个问题,对于你来说应该不难吧!

Input

每次输入一个数n(1<=n<=35),当n等于-1时结束输入。

Output

对于每个输入数据输出路径数,具体格式看Sample。

Sample

InputcopyOutputcopy
1 3 12 -1 
1 1 2 2 3 10 3 12 416024

卡特兰数公式

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	__int64 f[40]={0};
	f[0]=1,f[1]=1;
	for(int i=2;i<=35;i++){
		for(int j=0;j<i;j++){
			f[i]+=f[j]*f[i-j-1];
		}
	}
	int cnt=1,n;
	while(cin>>n){
		if(n==-1){
			break;
		}
		printf("%d %d %I64d\n",cnt++,n,f[n]*2);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值