输入n个句子(以标点符号断句),每个句子中有许多单词,输出每个句子的单词中有多少个名字。
名字的定义:以大写字母开头,其它都为小写字母的单词,可以为单个大写字母,末尾可以是标点符
取出每一个单词然后检验,注意单个的单词。
#include
#include
#include
using namespace std;
const int MAXN=int(1e5+5);
int n;
int ans[MAXN];
char s[MAXN];
bool Check(char s[],int len) {
if(len==1&&('A'<=s[1]&&s[1]<='Z'))
return 1;
if(!('A'<=s[1]&&s[1]<='Z'))
return 0;
for(int i=2;i<=len-1;i++)
if(!('a'<=s[i]&&s[i]<='z'))
return 0;
char E=s[len];
if(!(('0'<=E&&E<='9')||('A'<=E&&E<='Z')))
return 1;
return 0;
}
int main()
{
//freopen("imena.in","r",stdin);
//freopen("imena.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) {
int cnt=0;
bool f=0;
while(!f) {
scanf("%s",s+1);
int len=strlen(s+1);
ans[i]+=Check(s,len);
char E=s[len];
if(!(('0'<=E&&E<='9')||('a'<=E&&E<='z')||('A'<=E&&E<='Z')))
f=1;
}
}
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
输入一个 n ∗ m n*m n∗m的字符矩阵,从左上角走到右下角,每次向右或向下,所有经过的字符按经过顺序形成一个字符串,求出按此规则可以得到的最小字典序的字符串。
首先我想到的是类似经过路径求和的 D p Dp Dp
string dp[MAXN][MAXN];
for(int i=1;i<=n;i++)
dp[1][i]=dp[1][i-1]+Map[1][i];
for(int i=1;i<=m;i++)
dp[i][1]=dp[i-1][1]+Map[i][1];
for(int i=2;i<=n;i++)
for(int j=2;j<=m;j++)
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+Map[i][j];
然后愉快的打完,想了一下(想想都不敢想)
d p dp dp数组的空间是 M A X N ∗ M A X N ∗ s t r i n g . l e n g t h ( ) MAXN*MAXN*string.length() MAXN∗MAXN∗string.length()-------> M I L MIL MIL
所以就不能直接这样打对吧。
所以要解决的问题就是不能直接把结果存下来。
所以我们就想到把可以选择并进行转移的位置存下来就好了。
(所以三连,句式整齐,朗朗上口,用得好!)
实现上就是用一个 b o o l bool bool数组把上次转移可以继续转移的位置标记出来就行了。
#include
#include
#include
#include
using namespace std;
const int MAXN=int(2e3+5);
int n,m;
char Map[MAXN][MAXN];
string ans;
bool f[MAXN][MAXN];
int main()
{
freopen("pohlepko.in","r",stdin);
freopen("pohlepko.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",Map[i]+1);
ans=Map[1][1];
f[1][1]=1;
for(int k=3;k<=n+m;k++) {
char Min='z'+1;
for(int i=1;im) continue;
if(f[i-1][j]||f[i][j-1])
Min=min(Min,Map[i][j]);
}
for(int i=1;im) continue;
if(Map[i][j]==Min&&(f[i-1][j]||f[i][j-1]))
f[i][j]=1;
}
ans+=Min;
}
cout<
有 n n n个装着水的杯子,每个杯子的容积无限大,可以把一个杯子的水倒到另一个杯子里,是最后只剩下 k k k个杯子里有水。把第 i i i个杯子里的水倒进第 j j j个杯子需要的代价为 c [ i ] [ j ] c[i][j] c[i][j],求出完成倒水的最小代价。
注意:可以多次倒水,即允许先将 i i i倒进 j j j再将 j j j倒进 k k k
如同许多题一样,这个 ( 1 < = N < = 20 ) (1<=N<=20) (1<=N<=20)已经给了我们许多的提示,再看看题目,明显是状压 D p Dp Dp
先考虑状态定义——显然至于哪些杯子里仍然有水有关,表达的应该是到这个状态的代价
dp[S]:得到S(0表示有水,1表示没水)的最小代价
再考虑转移。贪心的想,每次倒水必定是倒进一杯有水的杯子,所以每次集合里的0是减少的。
看转移方程式,很好理解的:
就是枚举现在 S S S里还有水的杯子 i i i, j j j然后把 i i i里的水倒到 j j j里
#include
#include
#include
#include
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN=25;
int n,k;
int G[MAXN][MAXN];
int dp[1<<20];
int Check(int x) {
int ret=0;
while(x) {
if(x%2)
ret++;
x>>=1;
}
return ret;
}
int main()
{
freopen("kronican.in","r",stdin);
freopen("kronican.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&G[i][j]);
int S=(1<=n-k)
ans=min(ans,dp[s]);
printf("%d",ans);
}
难以言说,自己看题。
算了,还是稍微理一下,这道题的难点就在于题目的理解。
在连乘或连加时,每个数(假如有 k k k个)的和要小于等于 Z k Z_k Zk。
例如
( A 1 + A 2 + A 3 + . . . + A k ) (A_1+A_2+A_3+...+A_k) (A1+A2+A3+...+Ak)则要求 ∑ i = 1 n A i < = Z k \sum^{n}_{i=1}A_i<=Z_k ∑i=1nAi<=Zk
同理
( A 1 ∗ A 2 ∗ A 3 ∗ . . . ∗ A k ) (A_1*A_2*A_3*...*A_k) (A1∗A2∗A3∗...∗Ak)也要求 ∑ i = 1 n A i < = Z k \sum^{n}_{i=1}A_i<=Z_k ∑i=1nAi<=Zk
而单个的 ( x ) (x) (x)也要求 x < = Z 1 x<=Z_1 x<=Z1
#include
#include
#include
using namespace std;
#define DB double
const int MAXN=int(1e6+5),MAXK=55;
int k,len,r;
int z[MAXK];
char s[MAXN];
DB Dfs(int x) {
DB p[MAXK];
int cnt=0,f=0;
while(r<=len) {
r++;
if(s[r]=='?')
p[++cnt]=z[1];
if(s[r]=='*')
f=1;
if(s[r]==')') {
sort(p+1,p+cnt+1);
DB sum=0;
for(int i=1;i<=cnt;i++)
sum+=p[i];
sum=min(sum,(DB)z[cnt]);
if(!f)return sum;
else{
DB res=1;int i;
for(i=1;i<=cnt;i++) {
if(sum/(cnt-i+1)>p[i]) {
sum-=p[i];
res*=p[i];
}
else break;
}
for(int j=i;j<=cnt;j++)
res*=sum/(cnt-i+1);
return res;
}
}
if(s[r]=='(') {p[++cnt]=Dfs(x+1);}
}
return p[1];
}
int main()
{
freopen("kvalitetni.in","r",stdin);
freopen("kvalitetni.out","w",stdout);
scanf("%d",&k);
for(int i=1;i<=k;i++)
scanf("%d",&z[i]);
scanf("%s",s+1);
len=strlen(s+1);
r=0;
printf("%.5f",Dfs(0));
}
很清楚了,自己看。
很简单嘛,固定一个左端点,做两次 D p Dp Dp,一次最长上升,一次最长下降。
同时,统计出以每个点为端点的最长(上升/下降)子序列的个数(等会儿再用)
#include
#include
using namespace std;
#define MAXN LL(2e5+5)
#define Mod LL(1e9+7)
typedef long long LL;
LL n,A[MAXN],B[MAXN],n0;
LL Min,Max,len,m;
LL pow2[MAXN],ans;
struct node {
LL a;
LL b;
node() {a=b=0;}
}tree[2][MAXN];
node dp1[MAXN],dp2[MAXN];
node Add(node a,node b) {
if(a.a==b.a)
a.b=(a.b+b.b)%Mod;
if(b.a>a.a)
a=b;
return a;
}
void Insert(LL x,LL f,node D){
while(x<=n0&&x>0){
tree[f][x]=Add(tree[f][x],D);
x=x+(f?-1:1)*(x&(-x));
}
}
node Query(LL x,LL f){
node ret;
while(x<=n0&&x>0){
ret=Add(ret,tree[f][x]);
x=x+(f?1:-1)*(x&(-x));
}
return ret;
}
int main()
{
freopen("zoltan.in","r",stdin);
freopen("zoltan.out","w",stdout);
scanf("%lld",&n);
n0=n;
for(LL i=1;i<=n;i++)
scanf("%lld",&A[i]);
for(LL i=1;i<=n;i++)
B[i]=A[i];
sort(B+1,B+n+1);
n0=unique(B+1,B+n+1)-B-1;
for(LL i=1;i<=n;i++)
A[i]=lower_bound(B+1,B+n0+1,A[i])-B;
pow2[0]=1;
for(LL i=1;i<=n;i++)
pow2[i]=(pow2[i-1]*2)%Mod;
for(LL i=n;i>=1;i--) {
node p=Query(A[i]+1,1),s;
s.a=1,s.b=1;
if(!p.a)
dp1[i]=s,Insert(A[i],1,s);
else
p.a++,dp1[i]=p,Insert(A[i],1,p);
}
for(LL i=n;i>=1;i--) {
node p=Query(A[i]-1,0),s;
s.a=1,s.b=1;
if(!p.a)
dp2[i]=s,Insert(A[i],0,s);
else
p.a++,dp2[i]=p,Insert(A[i],0,p);
}
for(LL i=1;i<=n;i++)
m=max(m,dp1[i].a+dp2[i].a-1);
for(LL i=1;i<=n;i++)
if(dp1[i].a+dp2[i].a-1==m)
ans=(ans+((dp1[i].b*dp2[i].b)%Mod)*pow2[n-m]%Mod)%Mod;
printf("%lld %lld",m,ans);
}