题意: 有个人在一维无限平面的原点,现在有n张能力卡,每张卡有一个能力值len和代价co,花费co就能获得到达(x-len)或(x+len)的能力,问这个人能到达所有点的最小代价,或输出不能到达(-1)
n ≤ 300 n \leq 300 n≤300
前置知识:n个数的翡蜀定理(被喂题的时候同时被喂的这个,几乎就是告诉解法(不是
然而还是不会(你
就是求一个集合他们的共同最大公约数为1的最小代价
能想到这样子每个数就只和他的因数有关
然后我就卡住了 ,去看了题解
1e9之内每个数的质因数的种数最多是9,(因子个数好像是1600)
知道他不多之后就可以用mask dp(??
就是对每个数处理他的mask,然后dp求最小代价
感觉很妙
复杂度 O ( 2 9 × n 2 ) O(2^9 \times n^2) O(29×n2), 实际会更小点
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 310;
const int inf = 1e7;
int n;
int len[N], co[N], m[N];
int f[N][(int)(1 << 9) + 10];
int siz;
int ans = inf;
vector<int> vec;
int dp(int cur, int mask){
//mask dp(
if(cur == n) {
if(mask == (1 << siz) - 1) {
return 0;
}
return inf;
}
if(f[cur][mask] != -1) return f[cur][mask];
f[cur][mask] = inf;
f[cur][mask] = min(f[cur][mask], dp(cur + 1, mask));
f[cur][mask] = min(f[cur][mask], dp(cur + 1, mask| m[cur]) + co[cur]);
return f[cur][mask];
}
void getPrimes(int x) {
// 分解质因数
vec.clear();
for(int i = 2; i <= x / i; ++i) {
if(x % i == 0) {
vec.push_back(i);
while(x % i == 0) {
x /= i;
}
}
}
if(x > 1) {
vec.push_back(x);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 0; i < n; ++ i) {
cin >> len[i];
}
for(int i = 0; i < n; ++ i) {
cin >> co[i];
}
for(int i = 0; i < n; ++i) {
//在选第i数的情况下
getPrimes(len[i]);
siz = vec.size();
for(int j = 0; j < n; ++ j) {
// 对每个数做mask
m[j] = 0;
for(int k = 0; k < siz; ++ k) {
if(len[j] % vec[k] != 0)
m[j] |= (1 << k);
}
}
memset(f, -1, sizeof(f)); //初始化-1
int res = dp(0, 0) + co[i];
ans = min(ans, res);
}
if(ans == inf) {
cout << -1;
}
else {
cout << ans;
}
return 0;
}
朋友给了第二种做法:
不太会算复杂度只能说感觉最坏情况好像卡边,但是实际到不了所以很可(大概
O ( n 2 × 1600 ) − − O(n^2 \times 1600) -- O(n2×1600)−−
但是这里unordered_map遍历好像会有点问题
#include<bits/stdc++.h>
using namespace std;
const int maxn=305;
typedef long long ll;
ll l[maxn],c[maxn];
map<ll,ll>mp;
int main()
{
int n;
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++) scanf("%lld",&l[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
mp.clear();
mp[0]=0;//gcd(0,x)=x
//unordered_map<ll,ll>::iterator it;
for(int i=1;i<=n;i++) {
auto it = mp.begin();
while(it != mp.end()){
// cout << it->first << endl;
ll a=__gcd(l[i],it->first);
if(mp.count(a)>0)
mp[a]=min(mp[a],it->second+c[i]);
else
mp[a]=it->second+c[i];
++it;
}
}
if(mp[1]==0){
printf("-1\n");
}
else {
printf("%lld",mp[1]);
}
}
}