解法自体はすぐ思いつくので、むしろ計算量見積もりが本題かも。
http://community.topcoder.com/stat?c=problem_statement&pm=14767
問題
N要素の数列Xが与えられる。
この数列の異なる2要素を選んだ時、その積が平方数にならないようにしたい。
Xのうち1要素を1変化させるのに1コストがかかる。
また、Xの各要素は正の整数でなければならない。
条件を満たすようXを変化させるのに最小コストを求めよ。
解法
f(x) := xを平方数で割り切れるだけ割った値
とすると、(f(X[0]),f(X[1]),...)が異なるようにXを構成すればよい。
各要素同じf(X[i])が取れず、また1だけX[i]を動かすのに1コストがかかるので最小コストフローを使えばよい。
sourceからi番目の頂点に相当する要素に容量1の辺を張り、そこからf(X[i]+j)およびf(X[i]-j)に相当する点にコストjの辺を張る。
f(x)に相当する頂点からsinkに容量1の辺を張ればグラフの出来上がり。
なお、この際jは最大でも2N程度まで見ればよい。
const int prime_max = 1100000; int NP,prime[100000],divp[prime_max]; map<int,int> M; void cprime() { if(NP) return; for(int i=2;i<prime_max;i++) if(divp[i]==0) { prime[NP++]=i; divp[i]=i; for(ll j=1LL*i*i;j>=i&&j<prime_max;j+=i) if(divp[j]==0) divp[j]=i; } } int gettype(int v) { set<int> S; while(v>1) { int x=divp[v]; if(S.count(x)==0) S.insert(x); else S.erase(x); v/=x; } int ret=1; FORR(s,S) ret*=s; return ret; } template<int NV,class V> class MinCostFlow { public: struct edge { int to, capacity; V cost; int reve;}; vector<edge> E[NV]; int prev_v[NV], prev_e[NV]; V dist[NV]; void add_edge(int x,int y, int cap, V cost) { E[x].push_back((edge){y,cap,cost,(int)E[y].size()}); E[y].push_back((edge){x,0, -cost,(int)E[x].size()-1}); /* rev edge */ } V mincost(int from, int to, int flow) { V res=0; int i,v; ZERO(prev_v); ZERO(prev_e); while(flow>0) { fill(dist, dist+NV, numeric_limits<V>::max()/2); dist[from]=0; priority_queue<pair<V,int> > Q; Q.push(make_pair(0,from)); while(Q.size()) { V d=-Q.top().first; int cur=Q.top().second; Q.pop(); if(dist[cur]!=d) continue; if(d==numeric_limits<V>::max()/2) break; FOR(i,E[cur].size()) { edge &e=E[cur][i]; if(e.capacity>0 && dist[e.to]>d+e.cost) { dist[e.to]=d+e.cost; prev_v[e.to]=cur; prev_e[e.to]=i; Q.push(make_pair(-dist[e.to],e.to)); } } } if(dist[to]==numeric_limits<V>::max()/2) return -1; int lc=flow; for(v=to;v!=from;v=prev_v[v]) lc = min(lc, E[prev_v[v]][prev_e[v]].capacity); flow -= lc; res += lc*dist[to]; for(v=to;v!=from;v=prev_v[v]) { edge &e=E[prev_v[v]][prev_e[v]]; e.capacity -= lc; E[v][e.reve].capacity += lc; } } return res; } }; MinCostFlow<1100000,int> mcf; class SquareFreeSet { public: int findCost(vector <int> x) { int N=x.size(); cprime(); int i; FOR(i,1100000) mcf.E[i].clear(); for(i=1;i<=1020000;i++) mcf.add_edge(i,0,1,0); FOR(i,N) { mcf.add_edge(1050000,1050001+i,1,0); for(int j=-200;j<=200;j++) if(x[i]+j>0) { mcf.add_edge(1050001+i,gettype(x[i]+j),1,abs(j)); } } int ret=mcf.mincost(1050000,0,N); FOR(i,N) { int hoge=0,cost; FORR(e,mcf.E[1050001+i]) { if(e.to<1050000 && e.capacity==0) { hoge=e.to; cost=e.cost; break; } } cout<<x[i]<<" "<<cost<<" "<<hoge<<" "<<x[i]+cost<<" "<<x[i]-cost<<endl; } return ret; } } *まとめ 最大ケースでj=1000でも余裕があったのでそれで入れてしまったが、200でも割とギリギリだった。