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; }
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; }