笔试题整理


链接:https://www.nowcoder.com/discuss/615992?type=2
来源:牛客网
 

第一题

n副扑克,张数为m,大小为1~m,每幅扑克抽一张,求和恰好为k的组合数,结果对10e9+7取余数。
思路:动态规划。和为i,j副扑克,,dp[i][j] = dp[i-1][j-1]+...+dp[i-m][j-1](此处需要判断 i-m>0 )。初始化,j=1,i<=m,dp[i][j] = 1; i==j,dp[i][j] = 1,后面的情况不可能存在,所以推出循环。

public class Main {
static long MOD = 1000000007;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        for (int i = 0; i < t; i++) {
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            int k = scanner.nextInt();
            System.out.println(solution(m, n, k));
        }
    }

    public static int solution(int m, int n, int k) {
        if (k < n || k > n * m) return 0;
        long[][] dp = new long[k + 1][n + 1];
        for (int i = 1; i <= k; i++) {
            for (int j = 1; j <= n; j++) {
                if (i == j) {
                    dp[i][j] = 1;
                    //后面的结果不可能发生
                    break;
                } else if (j == 1) {
                    if (i <= m) dp[i][j] = 1;
                } else {
                    for (int p = 1; p <= m; p++) {
                        if (i - p <= 0)
                            break;
                        dp[i][j] += dp[i - p][j - 1];
                        dp[i][j] %= MOD;
                    }
                }
            }
        }
        return (int) (dp[k][n] % MOD);
    }
}

 

第二题

 

 

第一题 O(n*m*k) 二维dp
第二题 O(nlogn) sort+前缀和


数据规模:n, m < 100000, 暴力O(n^2)显然超时,所以我们得考虑nlogn或者线性算法才能AC

1. 我们选择一组零件(i,j) 应该是 选择min(a[i]+b[j], a[j]+b[i])作为当前组合的不稳定性
2. 那么由1可知当 a[i]+b[j] < a[j]+b[i] 时,应该选择左边的组合,移项得a[i]-b[i] < a[j] - b[j],移项的想法是如何得到度量零件本身的属性
3. 对所有零件以key=a[i]-b[i]进行排序得到新的顺序s,那么排序后,对于零件i,它与左边的零件组合,它自身应该选择b[i],与右边的零件组合,它自身选择a[i]
4. 所以有ans[i] = b[i] * (i-1) + a[i] * (n-i) + sum_a[1...i-1] + sum_b[i+1...n]
5. 至于零件冲突,在输入的时候ans[x]和ans[y]都减去min(a[x]+b[y], a[y]+b[x])即可
6. 排序时间复杂度排序O(nlogn),前缀和处理时间和计算ans时间为O(n),零件冲突处理O(m)

#include <cstdio>
#include <algorithm>
#include <cstring>

const int N = 100005;
int n, m;
int a[N], b[N], id1[N], id2[N];
int u[N], v[N];

int p;
long long suma, sumb, ans[N];

bool cmp1(int x, int y)
{
 return a[x] - b[x] < a[y] - b[y];
}
bool cmp2(int x, int y)
{
 return u[x] < u[y];
}
int main()
{
// freopen("data.in", "r", stdin);
 scanf("%d%d", &n, &m);
 for (int i = 0; i < n; ++i)
 {
  scanf("%d%d", &a[i], &b[i]);
  id1[i] = i;
 }
 for (int i = 0; i < m; ++i)
 {
  scanf("%d%d", &u[i], &v[i]);
  --u[i];
  --v[i];
  id2[i] = i;
 }
 for (int i = 0; i < m; ++i)
 {
  u[i + m] = v[i];
  v[i + m] = u[i];
  id2[i + m] = i + m;
 }
 m <<= 1;
 std::sort(id1, id1 + n, cmp1);
 std::sort(id2, id2 + m, cmp2);

// for (int i = 0; i < n; ++i)
//  printf("%d\n", id1[i]);
 p = 0;
 suma = 0;
 for (int i = 1; i < n; ++i)
 {
  sumb += b[id1[i]];
 }
// printf("%lld\n", sumb);

 for (int i = 0; i < n; ++i)
 {
  int idx1 = id1[i];
  int l = 0, r = m, mid, pos;
  while (l < r)
  {
   mid = (l + r) >> 1;
   if (u[id2[mid]] >= idx1)
   {
    r = mid;
    pos = mid;
   }
   else
   {
    l = mid + 1;
   }
  }
  while (u[id2[pos]] == idx1)
  {
   ans[idx1] -= std::min(a[idx1] + b[v[id2[pos]]], a[v[id2[pos]]] + b[idx1]);
   ++pos;
  }
  
//  if (idx1 == 2)
//   printf("%lld\n", ans[idx1]);
  
//  printf("%d %lld %lld\n", idx1, suma, sumb);
  
  ans[idx1] += suma;
  ans[idx1] += sumb;
  ans[idx1] += b[idx1] * i;
  ans[idx1] += a[idx1] * (n - i - 1);
  
  suma += a[idx1];
  sumb -= b[id1[i + 1]];
 }
 for (int i = 0; i < n; ++i)
  printf("%lld\n", ans[i]);

 return 0;
}

 

 

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> a(n), b(n);
    vector<int> del(n);
    int sup = 0;

    for (int i = 0; i < n; ++i) {
        cin >> a[i] >> b[i];
        sup += b[i];
        del[i] = b[i] - a[i];
    }

    vector<int> ans(n, sup);
    for (int i = 0; i < n; ++i)
        ans[i] += n * b[i];
    sort(del.begin(), del.end());
    vector<int> sum(n + 1, 0);
    for (int i = 0; i < n; ++i)
        sum[i + 1] = sum[i] + del[i];
    for (int i = 0; i < n; ++i) {
  int curDel = b[i] - a[i];
  int idx =  upper_bound(del.begin(), del.end(), curDel)- del.begin();
  ans[i] -= sum[n] - sum[idx];
  ans[i] -= idx * curDel;
  ans[i] -= b[i] + a[i];
 }
    for (int i = 0; i < m; ++i) {
         int l, r;
         cin >> l >> r;
         --l; --r;
         int curDel = min(a[l] + b[r], a[r] + b[l]);
         ans[l] -= curDel;
         ans[r] -= curDel;
     }
 for (int i = 0; i < n; ++i)
     cout << ans[i] << " ";
 return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值