标签:sum 要求 否则 += 开始 port 复杂度 rom 连续
题意:有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;
}
题意:你一开始有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;
}
最后这个题好搞啊,写了好久。
标签:sum 要求 否则 += 开始 port 复杂度 rom 连续
原文地址:https://www.cnblogs.com/diorvh/p/12129744.html