[解题报告]ural 1041 Nikifor
Abstract
ural 1041
贪心 矩阵胚
Body
Source
http://acm.timus.ru/problem.aspx?space=1&num=1041
Description
给定M个N维带权向量。求这些向量中的一个极大无关组(显然有N个向量),使得其权值和最小。
Solution
设S为所有给定的向量,I为这些向量构成的所有线性无关组。则M=(S,I)为一矩阵胚(算法导论翻译为拟阵),因此可采用贪心算法。将所有向量按权值从小到大排序,当前向量能加入解集就加入(与当前解集线性无关),否则舍弃。具体证明以后再写。判断线性相关用高斯消元,将当前向量每一维能消成0的消成0,如果最后不全为0就是线性无关。注意不要用浮点数,运算对质数取模即可。
因为下意识地以为会spj没看到要输出字典序最小的WA了整个晚上……
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL MOD = 971027;
const int MAXN = 55;
struct sv {int id, cost;}v[2020];
bool operator<(const sv &v1, const sv &v2)
{
if (v1.cost==v2.cost) return v1.id<v2.id;
return v1.cost < v2.cost;
}
int M, N;
LL a[2020][MAXN];
int first[2020];
int ref[55];
int cost;
vector<int> ans;
LL gcd(LL p, LL q)
{
LL r;
while (q)
{
r = p%q;
p = q;
q = r;
}
return p;
}
int gauss(int i)
{
int j, k;
for (j = 0; j < N; ++j)
{
if (a[i][j]==0) continue;
if (ref[j]==-1) return j;
LL t = gcd(abs(a[i][j]), abs(a[ref[j]][j]));
LL p = a[ref[j]][j]/t, q = a[i][j]/t;
for (k = j; k < N; ++k)
a[i][k] = (a[i][k]*p-a[ref[j]][k]*q)%MOD;
}
return -1;
}
int main()
{
int i, j, k;
scanf("%d%d", &M, &N);
for (i = 0; i < M; ++i)
for (j = 0; j < N; ++j)
scanf("%lld", &a[i][j]);
for (i = 0; i < M; ++i)
{
scanf("%d", &v[i].cost);
v[i].id = i;
}
sort(v, v+M);
memset(ref, 255, sizeof(ref));
for (i = 0; i < M; ++i)
{
j = gauss(v[i].id);
if (j != -1)
{
ref[j] = v[i].id;
cost += v[i].cost;
ans.push_back(v[i].id+1);
}
if (ans.size()==N) break;
}
if (ans.size()<N)
puts("0");
else
{
printf("%d\n", cost);
sort(ans.begin(), ans.end());
for (i = 0; i < N; ++i)
printf("%d\n", ans[i]);
}
return 0;
}