题意:[1..n].n个人.如果想要邀请第i个人 则必须要邀请第a[i]个人.
1<=k<=n<=1000. 邀请的人数不超过k时. 最多能邀请多少个人?
先建图,连接有向边(i,a[i]) 若邀请x 则要邀请以x为起点遍历的联通分量.
因为每个点只有一个出边. 所以每个联通分量都是以 一个环 + 若干个分支(tributary)组成.
选择该联通分量任意一点 都要把环选上 , 所以该联通分量可以选的数量为[环大小:分量大小]
令dp[i][j] 为前i个联通分量 是否能选出j个人. (背包问题).
复杂度:O(Σsz[i]*k)=O(n*k)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii;
const int N=2e3+5;
int n,k,a[N];
vector<ii> cycle;
int cid[N];// u belong to cycle c[id];
int calc(int s,int t,int id){
cid[s]=id;
if(s==t) return 1;
return 1+calc(a[s],t,id);
}
bool dp[N];
int main(){
// ios::sync_with_stdio(false);cin.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
memset(cid,-1,sizeof(cid));
for(int i=1;i<=n;i++){
int at=i;
if(cid[at]!=-1) continue;
bool vis[N]={0};
while(vis[at]==0){
vis[at]=true;
at=a[at];
}
int l=calc(a[at],at,cycle.size());
cycle.push_back(ii(l,0));
}
for(int i=1;i<=n;i++){
if(cid[i]!=-1) continue;
int at=i,cnt=0;
while(cid[at]==-1) at=a[at];
++cycle[cid[at]].second;
}
dp[0]=true;
for(auto p:cycle){
for(int j=k;j>=0;j--){
if(!dp[j]) continue;
for(int x=p.first;x<=p.first+p.second;x++)
dp[j+x]=true;
}
}
int res=k;
while(!dp[res]) res--;
cout<<res<<'\n';
return 0;
}