1004:Oh My Holy FFF
这题听人说是名次线段树,最后模仿别人的代码写的。
dp[i]=max{dp[j]-a[j])+a[i]*a[j]} a[j]<a[i]&&i-l<=j<=i-1;
1.将所有的点的值排名,所以每次求当前点的时候,只求1-当前排名-1的这些点的最大值,把所有不可能的状态都设置为-INF。
2.因为每次长度都不能超过l,所以每次把i-l设置为-INF。并且更新当前点的名次。
注意下初始化问题:
1.当前点i的位置小于等于l的话,可以作为一段的最后一个点,可能直接取dp[i]=a[i]*a[i];
2.当前点的排名为1的话,如果不是第一个点,不可能作为一段的最后一个点,dp[i]=-INF;
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 100005
#define INF 10000000005LL
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll __int64
using namespace std;
int a[maxn],ord[maxn],rank[maxn];
ll dp[maxn];
ll max(ll x,ll y)
{
return x>y?x:y;
}
bool cmp(int i,int j)
{
if(a[i]==a[j]) return i>j;
else return a[i]<a[j];
}
ll mx[maxn<<2];
void build(int l,int r,int rt)
{
mx[rt]=-INF;
if(l==r) return ;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void PushUp(int rt)
{
mx[rt]=max(mx[rt<<1],mx[(rt<<1)|1]);
}
void updata(int p,ll val,int l,int r,int rt)
{
if(l==r)
{
mx[rt]=val;
return ;
}
int m=(l+r)>>1;
if(p<=m)
{
updata(p,val,lson);
}
else
{
updata(p,val,rson);
}
PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return mx[rt];
int m=(l+r)>>1;
ll ret=-INF;
if(L<=m)
{
ret=max(ret,query(L,R,lson));
}
if(R>m)
{
ret=max(ret,query(L,R,rson));
}
return ret;
}
int main()
{
int t,tt=1;
scanf("%d",&t);
a[0]=0,dp[0]=0;
while(t--)
{
int n,l;
scanf("%d%d",&n,&l);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
build(1,n,1);
for(int i=1; i<=n; i++) ord[i]=i;
sort(ord+1,ord+1+n,cmp);
for(int i=1; i<=n; i++) rank[ord[i]]=i; //求出没个点的名次
for(int i=1; i<=n; i++)
{
int r=rank[i]-1;
ll tmp;
if(i-l<1)//如果长度小于l,可以把该位置作为一段的最后一个位置
{
dp[i]=0;
}
else
{
dp[i]=-INF;
}
if(r<=0)//如果是第一小的数,肯定不能作为某组的最后一个数
{
tmp=-INF;
}
else
{
tmp=query(1,r,1,n,1);
}
dp[i]=max(dp[i],tmp);
if(dp[i]==-INF)
updata(rank[i],-INF,1,n,1);
else
{
dp[i]+=(ll)a[i]*a[i];
updata(rank[i],dp[i]-a[i],1,n,1);
}
if(i-l>0)
{
updata(rank[i-l],-INF,1,n,1);
}
}
if(dp[n]==-INF)
printf("Case #%d: No solution\n",tt++);
else
printf("Case #%d: %I64d\n",tt++,dp[n]);
}
return 0;
}
1010:The Shortest Path in Nya Graph
hdu4725
这题一开始用spfa一直tle+wa,注意两点:
1.新增通过层来增加边,如果i属于第k层,一层分出入点和出点两个点,增加边i->n+(k-1)*2+1,n+(k-1)*2+2->i;
然后相邻层之间要增边,2*(k-1)+1->2*(k)+2,2*(k)+1->2*(k-1)+2;
2.spfa最坏的情况是O(m*n),这题m和n能达到10^5,果断会超。所以用优先级队列优化dijstra,能降到O(nlogn)。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 100005
#define INF 0x7fffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
using namespace std;
int dis[3*maxn],vis[3*maxn];
int head[3*maxn];
struct node
{
int v,w,nt;
} e[6*maxn];
struct point
{
int d,u;
bool operator< (const point& x)const
{
return d>x.d;//距离大的优先级小
}
};
priority_queue<point> q;
int id;
void add(int u,int v,int w)
{
e[id].v=v,e[id].w=w;
e[id].nt=head[u];
head[u]=id++;
}
void init(int n)
{
id=0;
memset(head,-1,sizeof(head));
for(int i=1; i<=3*n; i++)
{
dis[i]=INF;
}
memset(vis,0,sizeof(vis));
}
void dij()
{
dis[1]=0;
q.push((point){0,1});
while(!q.empty())
{
point tmp=q.top();q.pop();
if(vis[tmp.u]) continue;
vis[tmp.u]=1;
for(int i=head[tmp.u];i!=-1;i=e[i].nt)
{
int v=e[i].v,w=e[i].w;
if(dis[tmp.u]+w<dis[v])
{
dis[v]=dis[tmp.u]+w;
q.push((point){dis[v],v});
}
}
}
}
//void spfa()
//{
// queue<int> q;
// dis[1]=0;
// q.push(1);
// vis[1]=1;
// while(!q.empty())
// {
// int u=q.front();
// q.pop();
// for(int i=head[u]; i!=-1; i=e[i].nt)
// {
// int v=e[i].v,w=e[i].w;
// if(dis[u]+w<dis[v])
// {
// dis[v]=dis[u]+w;
// if(!vis[v])
// {
// vis[v]=1;
// q.push(v);
// }
// }
// }
// vis[u]=0;
// }
//}
int main()
{
int n,m,t,c,tt=1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&c);
init(n);
int level,p1,p2,p;
for(int i=1; i<=n; i++)
{
scanf("%d",&level);
p1=n+(level-1)*2+1,p2=p1+1;
add(i,p1,0);
add(p2,i,0);
}
int pp1,pp2;
for(int i=1; i<n; i++)
{
p1=n+(i-1)*2+1,p2=p1+1;
pp1=n+i*2+1,pp2=pp1+1;
add(p1,pp2,c);
add(pp1,p2,c);
}
int u,v,w;
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dij();
if(dis[n]==INF)
printf("Case #%d: %d\n",tt++,-1);
else
printf("Case #%d: %d\n",tt++,dis[n]);
}
return 0;
}
1007:Kia's Calculation
hdu4722
比较裸的数位dp吧,dp(i,j)表示长度为i位,数位和模10余j的个数。状态方程:dp(i,j)=sum(dp(i-1,10-k-j+10)%10) (0<=k<=9)
ans=sum(dp(i-1,10-m-k)) i从数位的总长度到0,如果是最后一位,0<=k<= a[i],否则0<=k<a[i];因为只有最后一位,才可以取到改位的数,高位取0的情况就是改位没有数。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 10005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll __int64
using namespace std;
int a[20];
ll f[20][20];
ll DP(int i,int j)
{
if(i==0) return j==0?1:0;
if(f[i][j]>=0) return f[i][j];
ll &ret=f[i][j];
ret=0;
for(int k=0;k<10;k++)
{
ret+=DP(i-1,((j-k)%10+10)%10);
}
return ret;
}
ll count(ll x)
{
if(x==0) return 1;
if(x<0) return 0;
int id=0;
while(x)
{
a[id++]=x%10;
x/=10;
}
ll ret=0,m=0;
for(int i=id-1;i>=0;i--)
{
if(!i)
{
for(int j=0;j<=a[i];j++)
{
ret+=DP(i,((10-m-j)%10+10)%10);
}
}
else
{
for(int j=0;j<a[i];j++)
{
ret+=DP(i,((10-m-j)%10+10)%10);
}
}
m=(m+a[i])%10;
}
return ret;
}
void init()
{
memset(f,-1,sizeof(f));
}
int main()
{
int t;
scanf("%d",&t);
init();
int tt=1;
while(t--)
{
ll a,b;
scanf("%I64d%I64d",&a,&b);
printf("Case #%d: %I64d\n",tt++,count(b)-count(a-1));
}
return 0;
}
给你两个数,任意位都可以随意调换,不能出现前置0.
用pre_permutation,next_permutation暴力肯定会超时,其实只要记录每个数字出现的次数,从9开始,找出所有的可能和模10为9的组合,然后该组合的数字减减。
hdu4726:
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 1000005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll __int64
using namespace std;
char s1[maxn],s2[maxn],s[maxn];
int a1[10],a2[10];
int main()
{
int t,tt=1;
scanf("%d",&t);
while(t--)
{
scanf("%s",s1);
scanf("%s",s2);
printf("Case #%d: ",tt++);
int l1=strlen(s1);
memset(a1,0,sizeof(a1));
int l2=strlen(s2);
memset(a2,0,sizeof(a2));
for(int i=0;i<l1;i++)
{
a1[s1[i]-'0']++;
}
for(int i=0;i<l2;i++)
{
a2[s2[i]-'0']++;
}
int flag=0,id=0,cnt=0;
for(int sum=9;sum>0;sum--)
{
for(int i=9;i>=0;i--)
{
for(int j=9;j>=0;j--)
{
if(a1[i]>0&&a2[j]>0&&(i+j)%10==sum&&i!=0&&j!=0)
{
a1[i]--;
a2[j]--;
s[id++]=char(sum+'0');
flag=1;
break;
}
}
if(flag==1) break;
}
if(flag==1) break;
}
if(flag==0)
{
printf("%d\n",(s1[0]-'0'+s2[0]-'0')%10);
continue;
}
for(int sum=9;sum>=0;sum--)
{
for(int i=9;i>=0;i--)
{
for(int j=9;j>=0;j--)
{
while((i+j)%10==sum&&a1[i]>0&&a2[j]>0)
{
a1[i]--;a2[j]--;
s[id++]=char(sum+'0');
}
}
}
}
s[id]='\0';
printf("%s\n",s);
}
return 0;
}