NCPC 14G. Outing 图论(出边),DP

题意:[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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值