【數學 線性基】[JLOI2015]裝備購買
阿新 • • 發佈:2022-02-04
傳送門:
https://www.acwing.com/problem/content/description/211/
分析
採取這樣的貪心策略:將物品看作是矩陣中的行,按照花費升序排序,然後從 \(1-n\) 掃描,當第 \(i\) 個行和前面加入的所有行線性無關的時候,就將其花費計入答案,反之不計入。
這樣做為什麼是對的呢?採用歸納法來證明:
下證:前 \(n\) 行採取上述策略能夠在保證選出的行構成的線性空間與前 \(n\) 行構成的線性空間相等的前提下花費最小。
- 前 \(1\) 行,我們肯定需要將第一行加入貢獻,滿足。
- 假設前 \(k\) 行滿足上述貪心策略。
- 下只需證明前 \(k+1\)
- 如果前 \(k\) 行構成的線性空間和前 \(k+1\) 行構成的線性空間相等(也就是第 \(k+1\) 行能被前 \(k\) 行選出的行線性表出),那麼我們肯定不選取,滿足最優。
- 如果不相等,也就是第 \(k+1\) 行與前 \(k\) 行線性無關。假設我們不選取第 \(k+1\) 行,那麼無論如何從前 \(k\) 行進行選取也不能使得選出的行構成的線性空間與前 \(k+1\) 行構成的線性空間相等,因此必須選。
實現
#include<bits/stdc++.h> using namespace std; #define debug(x) cerr << #x << ": " << (x) << endl #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define dwn(i,a,b) for(int i=(a);i>=(b);i--) #define pb push_back #define all(x) (x).begin(), (x).end() using ll = long long; inline void read(int &x){ int s=0; x=1; char ch=getchar(); while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();} while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); x*=s; } const int N=550; const double eps=1e-5; int n, m; int w[N][N], c[N]; int idx[N]; bool zero(double x){ return abs(x)<eps; } bool zero(vector<double> a){ for(auto i: a) if(!zero(i)) return false; return true; } void change(vector<double> &a, vector<double> &b){ rep(i,0,m-1){ if(zero(a[i]) && !zero(b[i])) return; if(!zero(a[i]) && zero(b[i])){ swap(a, b); return; } if(!zero(a[i]) && !zero(b[i])){ double rate=a[i]/b[i]; rep(j,i,m-1) a[j]-=rate*b[j]; return; } } } int main(){ cin>>n>>m; rep(i,1,n) rep(j,1,m) read(w[i][j]); rep(i,1,n) read(c[i]); rep(i,1,n) idx[i]=i; sort(idx+1, idx+1+n, [](int x, int y){ return c[x]<c[y]; }); vector<vector<double>> a; ll res=0; rep(i,1,n){ int p=idx[i]; vector<double> row; rep(j,1,m) row.pb(w[p][j]); for(auto &vec: a) change(row, vec); if(!zero(row)) a.pb(row), res+=c[p]; } cout<<a.size()<<' '<<res<<endl; return 0;