Diorvh

导航

【日记】1.1

1.1

贪心

1.1283E:New Year Parties

题意:有n个人,分别在1-n之间的点上,每个人最多往左或往右走一个格子,问走完之后最少占据格子和最多占据格子数分别是多少。

思路:贪心。最多:考虑每个位置怎么被占据,首先判断前面一个格子,如果还有剩的就拉过来,再看当前格子,如果有就占据,再看右边的格子,如果有就拉过来。可以用反证法说明这么做是最优的。

最少:考虑每个位置的数怎么丢出去。vis数组记录每个位置被占据的情况。如果前一个位置必须被占据,就丢到上一个。否则就丢到下一个位置,并占据下一个位置。可以用反证法说明这么做是最优的。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
#define db(x) cout<<#x<<":"<<x<<endl;
const int M=2e5+20,P=1e9+7;
struct TTTT{
	int n,num1[M],num2[M],vis[M];
	void init(){
		scanf("%d",&n);
		for(int i=1;i<=n;++i){
			int c;
			scanf("%d",&c),++num1[c];
		}
		for(int i=1;i<=n;++i)
			num2[i]=num1[i];
	}
	void run(){
		init();
		int ans=0;
		for(int i=1;i<=n;++i){
			if (num2[i]==0||vis[i])
				continue;
			if(vis[i-1])
				num2[i]=0;
			else
				vis[i+1]=1,++ans;
		}
		printf("%d ",ans);
		ans=0;
		if (num1[1])
			--num1[1],++ans;
		for(int i=1;i<=n;++i)
			if (num1[i-1])
				--num1[i-1],++ans;
			else if (num1[i])
				--num1[i],++ans;
			else if (num1[i+1])
				--num1[i+1],++ans;
		if (num1[n])
			--num1[n],++ans;
		printf("%d\n",ans);
	}
}TTT;
int main(){
	TTT.run();
	return 0;
}

DP

1.1271D:Portals

题意:你一开始有k个兵,要顺次打n个城堡,打完城堡之后不会损失兵。每个城堡有a,b,c三个参数,表示必须拥有>=a个兵才能打下来,打完之后可以获得b个兵,之后如果分出来1个兵占领这个城堡,那么就可以获得c点分数,如果不占领就不获得分数。

这里还有一些神奇的portal,有u和v两个参数,表示当你站在u城堡的时候,可以传送一个兵到v去占领v。注意不可以连续传送,比如你站在4,不可以4-3再3-1,只能用所在城堡直接相连的portal。

要求必须n个城堡全部打下来才可以,如果不能,输出-1,否则输出打完n个城堡之后可以获得的最大分数。

思路:我是DP做法。首先不考虑portal,那么dp[i] [j]表示打完前i个城堡之后,还剩下j个兵的状态,能得到的最大分数。那么刷表即可,每一个状态可以有两种选择,打完留兵或者打完不留,dp[i] [j+b[i]]=max(当前, dp[i-1] [j]),dp[i] [j+b[i]-1]=max(当前, dp[i-1] [j]+c[i])。时间复杂度\(O(n\sum k)\)

现在考虑portal。如果不占领第i个城堡,就不影响。利用贪心易知,如果要派兵占领第i个城堡,而且存在portal使得可以从之后的城堡j,k,l,……传送回来,那么选择之后城堡里面编号最大的那个往回传送一定是最优的。感性理解一下,留兵的唯一不利是有可能往前打的过程中兵不够打不了了,所以上述选择能够让那个本来直接留下的兵多打几轮(而且是尽可能多打了),再回来防守,所以肯定不会变差。

所以现在走到第i个城堡之后,选择变多了,首先是是否在本地留兵,之后是往回传送几个兵。

v[i]表示城堡i往回传送的目的地(可以看代码看具体意义)。那么还是贪心,肯定先选那些c值大的城堡传送,因此就先排序,再前缀和,就依次是往回传送1,2,3,4,5……个兵的最大获得的分数了。

细节比较多,需要分类讨论一下,详见代码。

时间复杂度不变,仍然是\(O(n\sum k)\),只不过要注意,这里的dp[i] [j]不再是原来子问题了,即前i的城堡剩下j个兵的最大分数,这是因为已经贪心处理portal的原因。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
#define db(x) cout<<#x<<":"<<x<<endl
#define qmax(x,y) (x)=max((x),(y))
const int M=5e3+50,P=1e9+7;
int n,m,k,a[M],b[M],c[M],dp[M][M],pfrom[M];
vector<int> v[M];
struct cmp{
	bool operator()(int x,int y){
		return c[x]>c[y];
	}
};
struct TTTT{
	void init(){
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=n;++i)
			scanf("%d%d%d",&a[i],&b[i],&c[i]);
		for(int i=1;i<=m;++i){
			int j,k;
			scanf("%d%d",&j,&k),pfrom[k]=max(pfrom[k],j);
		}
		for(int i=1;i<=n;++i)
			if(pfrom[i])
				v[pfrom[i]].push_back(i);
	}
	void run(){
		init();
		int kmin=k,kmax=k;
		for(int i=1;i<=n;++i){
			kmin=max(kmin,a[i]);
			if (kmin>kmax){
				printf("-1\n");
				return;
			}
			if (!v[i].empty()){
				sort(v[i].begin(),v[i].end(),cmp());
				v[i][0]=c[v[i][0]];
				for(int k=1;k<v[i].size();++k)
					v[i][k]=c[v[i][k]]+v[i][k-1];
			}
			for(int j=kmin;j<=kmax;++j){
				qmax(dp[i][j+b[i]],dp[i-1][j]);
				if (!v[i].empty())
					for(int k=0;k<v[i].size()&&j+b[i]-k-1>=0;++k)
						qmax(dp[i][j+b[i]-k-1],dp[i-1][j]+v[i][k]);
				if (!pfrom[i]){
					if (j+b[i]-1>=0)
						qmax(dp[i][j+b[i]-1],dp[i-1][j]+c[i]);
					if (!v[i].empty())
						for(int k=0;k<v[i].size()&&j+b[i]-1-k-1>=0;++k)
							qmax(dp[i][j+b[i]-1-k-1],dp[i-1][j]+c[i]+v[i][k]);
				}
			}
			kmax+=b[i],kmin+=b[i]-v[i].size()-(!pfrom[i]?1:0);
		}
		int ans=0;
		for(int i=kmin;i<=kmax;++i){
			qmax(ans,dp[n][i]);
		}
		printf("%d\n",ans);
	}
}TTT;
int main(){
	TTT.run();
	return 0;
}

最后这个题好搞啊,写了好久。

posted on 2020-01-01 20:11  diorvh  阅读(163)  评论(0编辑  收藏  举报