http://codeforces.com/contest/1107
给出一个数字要求将其分成数段,要保证后面的数总是大于前面的数,如不能分则输出NO
#include
#include
using namespace std;
char num[305];
int main()
{
int q;
scanf("%d",&q);
while(q--)
{
int len;
scanf("%d",&len);
scanf("%s",num);
if(len==2)
{
if(num[1]<=num[0]) {printf("NO\n");continue;}
}
printf("YES\n");
printf("2\n");
printf("%c %s\n",num[0],num+1);
}
}
一个数的根数是指其迭代各位相加的数最终成为一位数的数,现要求第k个数其数根为x
#include
using namespace std;
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
long long k,t;
scanf("%lld%lld",&k,&t);
printf("%lld\n",(k-1)*9+t);
}
}
给出一个字符串,与之相匹配的有一个伤害串,摁下字符串上的字符会造成伤害串上相应位置的伤害,操作者必须按照字符串的顺序进行摁键,但是可以跳过一些字符,问同一个字符被摁不超过k次的情况下,如何打出最高伤害
#include
#include
#include
#define int long long
using namespace std;
priority_queue<int> q;
int arr[200005];
char s[200005];
int32_t main()
{
int k,n;
scanf("%lld%lld",&k,&n);
for(int i=1;i<=k;i++)
scanf("%lld",&arr[i]);
scanf("%s",s+1);
long long ans=0;
for(int i=1;i<=k;i++)
{
if(i==1||s[i]==s[i-1]) q.push(arr[i]);
else
{
int tot=n;
while(!q.empty())
{
if(tot)
{
ans+=q.top();
tot--;
}
q.pop();
}
q.push(arr[i]);
}
}
int tot=n;
while(!q.empty())
{
if(tot)
{
ans+=q.top();
tot--;
}
q.pop();
}
printf("%lld\n",ans);
}
假如一个由16进制代表的2进制矩阵中的每个点都可以通过 A [ i ] [ j ] = B [ [ i x ] ] [ [ i x ] ] A[i][j]=B[[\frac{i}{x}]][[\frac{i}{x}]] A[i][j]=B[[xi]][[xi]]表示,则称这个矩阵A可以被x表示
转成2进制矩阵之后找出行和列中所有的连续相同的字符的长度,求一个最大公因数即可
#include
#include
#include
using namespace std;
char m[5210][1310];
bool ma[5210][5210];
bool num[5210];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",m[i]+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n/4;j++)
{
int v;
if(m[i][j]>='0'&&m[i][j]<='9') v=m[i][j]-'0';
else v=m[i][j]-'A'+10;
for(int k=4;k>=1;k--)
{
ma[i][(j-1)*4+k]=v&1;
v>>=1;
}
}
}
int col1=0;int col2;
for(int i=1;i<=n;i++)
{
col1=0;col2=0;
for(int j=1;j<=n;j++)
{
if(j==1||ma[i][j]==ma[i][j-1]) col1++;
else num[col1]=1,col1=1;
if(j==1||ma[j-1][i]==ma[j][i]) col2++;
else num[col2]=1,col2=1;
}
num[col1]=1,num[col2]=1;
}
int ans=-1;
for(int i=1;i<=5200;i++)
{
if(!num[i]) continue;
if(ans==-1) ans=i;
else ans=gcd(ans,i);
}
printf("%d\n",ans);
}
给出一串01串,相同的字符可以消去,消去后被消去子串两边将重新连接到一起,每消去一个长度x的可以得到一个收益 v x v_x vx问最大可以获得的收益
首先预处理出每一个长度的最优消除价值,根据左右端点和剩余的未消去的在相同字符连续区段间跳跃地记忆化搜索.即可得出答案
#include
#include
#define int long long
using namespace std;
char s[105];
int dp[105][105][105];
int cnt[105];
int tot=0;
int arr[105];
int dfs(int l,int r,int rest)
{
if(dp[l][r][rest]) return dp[l][r][rest];
if(l==r) return dp[l][r][rest]=arr[cnt[r]+rest];
dp[l][r][rest]=dfs(l,r-1,0)+arr[cnt[r]+rest];
for(int i=r-2;i>=l;i-=2) dp[l][r][rest]=max(dp[l][r][rest],dfs(l,i,rest+cnt[r])+dfs(i+1,r-1,0));
return dp[l][r][rest];
}
int32_t main()
{
int n;
scanf("%lld",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
for(int i=2;i<=n;i++) for(int j=1;j<i;j++) arr[i]=max(arr[i],arr[i-j]+arr[j]);
int j,i;
for(i=1;i<=n;i=j)
{
for(j=i;s[i]==s[j];j++);
cnt[++tot]=j-i;
}
printf("%lld\n",dfs(1,tot,0));
}
给出n只贷款,每只贷款在贷款初可以得到 a i a_i ai的钱,而在贷款过后接下来的 k i k_i ki个月,每个月需要付出 b i b_i bi的利息,在每个月最多借一种贷款的前提下,问如何借贷可以使得手中持有的钱的峰值最大
思路1:对贷款和贷款后到达峰值的时间连边,然后跑出最小费用流即可(时间卡得比较紧,我自己的费用流板子怎么都过不掉但是确实有人是这么过的)
思路2:思路大致上同,不过做二分图匹配
思路3:对每种贷款据其每月还贷的金额从大到小排序,设贷款后到达峰值的时间为x,对x进行dp .对每个i,对其x倒叙更新 d p [ x + 1 ] = m a x ( d p [ x + 1 ] , d p [ x ] + a [ i ] − b [ i ] ∗ x ) dp[x+1]=max(dp[x+1],dp[x]+a[i]-b[i]*x) dp[x+1]=max(dp[x+1],dp[x]+a[i]−b[i]∗x)意为剩余x+1天如借i贷可以使得总的钱数增加则,购买.再更新 d p [ x ] = m a x ( d p [ x ] , d p [ x ] + a [ i ] − b [ i ] ∗ k [ i ] ) dp[x]=max(dp[x],dp[x]+a[i]-b[i]*k[i]) dp[x]=max(dp[x],dp[x]+a[i]−b[i]∗k[i])即剩余天数剩余x天时,为其设定上限值a[i]-b[i]*k[i]由此将贷款有选择地安排入n个天数中,最终被更新到的即为答案
#include
#include
#include
#include
using namespace std;
#define int long long
int dp[505];
int a[505],b[505],c[505];
int no[505];
int32_t main()
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
for(int i=1;i<=n;i++) no[i]=i;
sort(no+1,no+1+n,[&](const int &i,const int &j){return b[i]>b[j];});
int ans=0;
memset(dp,0,sizeof(dp));
for(int k=1;k<=n;k++)
{
for(int j=n-1;j>=0;j--)
{
int i=no[k];
dp[j+1]=max(dp[j+1],dp[j]+a[i]-b[i]*j);
dp[j]=max(dp[j],dp[j]+a[i]-b[i]*c[i]);
ans=max({ans,dp[j],dp[j+1]});
}
}
cout<<ans<<endl;
}
给出一个长度为n的试题序列,每个试题有一个难度值 d i d_i di价格 c i c_i ci,每选择一个试题所得到的的收益为
问选择一段试题序列以求获得最大收益
对段(l,r)的收益可以写成 ∑ i = 1 r ( a − c i ) − ∑ i = 1 l ( a − c i ) − g a p ( l , r ) \sum_{i=1}^{r} (a-c_i)-\sum_{i=1}^{l}(a-c_i)-gap(l,r) ∑i=1r(a−ci)−∑i=1l(a−ci)−gap(l,r)
因此只需要对每个i维护出 l < r , m i n ( ∑ i = 1 l ( a − c i ) + g a p ( l , r ) ) l<r,min(\sum_{i=1}^{l}(a-c_i)+gap(l,r)) l<r,min(∑i=1l(a−ci)+gap(l,r))即可
其中 ∑ i = 1 l ( a − c i ) \sum _{i=1}^{l}(a-c_i) ∑i=1l(a−ci)可以较简单地直接通过前缀和维护,而 g a p ( l , r ) gap(l,r) gap(l,r)则可以通过对选择每一个i向前直到第一个j ( d j + 1 − d j ) 2 (d_{j+1}-d_j)^2 (dj+1−dj)2比第i项大的,其间(j+1,i)的gap(j+1,i)就都为 ( d i + 1 − d i ) 2 (d_{i+1}-d_i)^2 (di+1−di)2再通过简单的相加就可以得到(j+1,r)之间最小的 ∑ i = 1 l ( a − c i ) + g a p ( l , r ) \sum_{i=1}^{l}(a-c_i)+gap(l,r) ∑i=1l(a−ci)+gap(l,r)而随着右端点的移动可以不断更新最新的min而将原来的删去,为此我们需要用到multiset和stack维护.详情见代码
#include
#include
#include
#include
#define int long long
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
const int size=3e5+5;
stack<pii> Sta;
multiset<LL> ms;
LL c[size];int d[size];
inline LL calc(int i){return (d[i+1]-d[i])*(d[i+1]-d[i]);}
int32_t main()
{
int n,a;
LL ans=0;
scanf("%lld%lld",&n,&a);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&d[i],&c[i]);
c[i]= c[i-1]+a-c[i];ans=max(ans,c[i]-c[i-1]);
}
for(int i=1;i<n;i++)
{
if(!ms.empty()) ans=max(ans,c[i]-*ms.begin());
LL loop=calc(i),bs=c[i-1];
while(!Sta.empty()&&calc(Sta.top().first)<=loop)
bs=min(bs,Sta.top().second),ms.erase(ms.find(Sta.top().second+calc(Sta.top().first))),Sta.pop();
Sta.push(pii(i,bs)),ms.insert(loop+bs);
}
if(!ms.empty()) ans=max(ans,c[n]-*ms.begin());
printf("%lld\n",ans);
}