UVA 部分刷题记录

UVA10780 Again Prime? No Time.  打表找规律                思维

题意:求

题解:数学,题解见:http://blog.cinema000.xyz/

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M = 10005;
int tot, ans, cnt[M], yz[M];
void init(){
    tot = 0;
    ans = 1e9 + 7;
}
bool isprime(int x){
    for(int i = 2; i * i <= x; i++)
        if(x % i == 0)return 0;
    return 1;
}
void divide(int x){
    for(int i = 2; i * i <= x; i++)
        if(x % i == 0){
            yz[++tot] = i; cnt[tot] = 0;
            while(x % i == 0) x /= i, cnt[tot]++;
        }
    if(x != 1)yz[++tot] = x, cnt[tot] = 1;
}

int get(int x, int n){
    int k = 0, now = 1, ret = 0;
    while(1){
        now *= x, k++;
        int d = n / now;
        if(!d)break;
        ret += d;
    }
    return ret;
}

int main(){
    int T, idc = 0, m, n;
    scanf("%d", &T);
    while(T--){
        init();
        scanf("%d%d", &m, &n);
        printf("Case %d:\n", ++idc);
        if(isprime(m)){
            int ans = get(m, n);
            ans ? printf("%d\n", ans) : puts("Impossible to divide");
        }
        else {
            divide(m);
            for(int i = 1; i <= tot; i++)
            ans = min(ans, get(yz[i], n) / cnt[i]);
            ans ? printf("%d\n", ans) : puts("Impossible to divide");
        }
    }
}
View Code

 

 UVA10859 Placing Lampposts  有借鉴价值的建模            思维

给定一个n个点m条边的无向无环图,在尽量少的节点上放灯,使得所有边都与灯相邻(被灯照亮)。

在灯的总数最小的前提下,被两盏灯同时照亮的边数应该尽可能大。

题解:这道题转化为 在灯的总数最小的前提下,被一盏灯照亮的边数应该尽可能小, 就成了一个经典模型

化为求 z = M * x + y;  M是一个极大的数, 使的y的影响在x下面就没有用了;x为灯的总数,  y为被一盏灯照亮的边数, 用上司的舞会做一个函数最大值;

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M = 1005, mod = 5000;
int h[M], dp[M][2], tot; bool vis[M];
struct edge{int nxt, v;}G[M << 1];
void add(int u, int v){
    G[++tot].v = v; G[tot].nxt = h[u]; h[u] = tot;
}

void init(){
    memset(h, 0, sizeof(h));
    tot = 0;
    memset(vis, 0, sizeof(vis));
}


void dfs(int u, int f){
    vis[u] = 1;
    int tmp1 = 0, tmp2 = 0;
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(v == f)continue;
        dfs(v, u);
        tmp1 += dp[v][1] + 1;
        tmp2 += min(dp[v][1], dp[v][0] + 1);
    }
    dp[u][0] = tmp1;
    dp[u][1] = mod + tmp2;
}


int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        init();
        int n, m, u, v, ans = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++){
            scanf("%d%d", &u, &v);
            u++, v++;
            add(u, v), add(v, u);
        }
        for(int i = 1; i <= n; i++) 
            if(!vis[i]){
                dfs(i, i);
                ans += min(dp[i][0], dp[i][1]);
            }
        printf("%d %d %d\n",ans / mod, m - ans % mod, ans % mod);
    }
}
View Code

 

UVA11134 Fabled Rooks  分开考虑+贪心                            思维

在一个n*n(1<=n<=5000)的棋盘上放置n个车,每个车都只能在给定的一个矩形(xli,xri,yli,yri)里放置,使其n个车两两不在同一行和同一列,判断并给出解决方案。

题解:x,y互不影响,x,y分别做一次类似区间覆盖的贪心,如果x,y都合法,那么这个点的唯一位置就可以确定了;

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M = 5005;
int tox[M], toy[M];

struct Node{int id, xl, yl, xr, yr;}p[M];
bool cmp1(Node A, Node B){return A.xl == B.xl ? A.xr < B.xr : A.xl < B.xl;}
bool cmp2(Node A, Node B){return A.yl == B.yl ? A.yr < B.yr : A.yl < B.yl;}


int main()
{
    //puts("INPUT:");
    int n, idc = 0;
    while(scanf("%d", &n) == 1 && n){
        int xl, yl, xr, yr;
        for(int i = 1; i <= n; i++){
            scanf("%d%d%d%d", &xl, &yl, &xr, &yr);
            p[i] = (Node){i, xl, yl, xr, yr};
        }
        bool fg = 1;
        memset(tox, 0, sizeof(tox));
        memset(toy, 0, sizeof(toy));
        sort(p + 1, p + 1 + n, cmp1);
        for(int i = 1, t = 0; i <= n && t <= n; i++){
            while((++t) <= n)
                if(i >= p[t].xl && i <= p[t].xr){
                    tox[p[t].id] = i; break;
                }
            for(int x = t + 1; x <= n; x++) {
                if(p[x].xl > i)break;
                p[x].xl = i + 1;
            }
            if(t < n) sort(p + t + 1, p + n + 1, cmp1);
        }
        
        for(int i = 1; i <= n; i++)fg &= (tox[i] != 0);
        if(!fg){puts("IMPOSSIBLE");continue;}

        sort(p + 1, p + 1 + n, cmp2);
        for(int i = 1, t = 0; i <= n && t <= n; i++){
            while((++t) <= n)
                if(i >= p[t].yl && i <= p[t].yr){
                    toy[p[t].id] = i; break;
                }
            for(int x = t + 1; x <= n; x++) {
                if(p[x].yl > i)break;
                p[x].yl = i + 1;
            }
            if(t < n)sort(p + t + 1, p + n + 1, cmp2);
        }//for(int i = 1; i <= n; i++)printf("%d ", toy[i]);
        
        for(int i = 1; i <= n; i++)fg &= (toy[i] != 0);
        if(!fg){puts("IMPOSSIBLE");continue;}
        for(int i = 1; i <= n; i++)printf("%d %d\n", tox[i], toy[i]);

    }
    return 0;
}
View Code

 

 UVA10795 A Different Task   经典递归思路                     思维

题意:给出汉诺塔的初末状态,问最少多少步可以移到目标状态;

题解:首先大的并且已经在目标位置上的肯定不会移动,我们就只需要将k-1根移动到过度柱子 + 最后把k-1根从过度柱子移到目标状态 + 1;

这个用一个dfs(now, k, final)实现,还是先把k - 1移到过度柱子,此时过度柱子上的一定是从小到大,我们再把这k-1根移到final上,这个时候相当于普通的汉诺塔;步数是2^(k-1) + 1(移动一根k的步数);

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int pos[80], tgr[80];
#define ll long long
ll dfs(int * P, int k, int goal){
    if(!k) return 0;
    if(P[k] == goal) return dfs(P, k - 1, goal);
    return dfs(P, k - 1, 6 - P[k] - goal) + (1LL << (k - 1));
}



int main()
{
    int n, idc = 0;
    while(scanf("%d", &n) == 1 && n){
        ll ans = 0;
        for(int i = 1; i <= n; i++)scanf("%d", &pos[i]);
        for(int i = 1; i <= n; i++)scanf("%d", &tgr[i]);
        for(int i = n; i; i--){
            if(pos[i] != tgr[i]){
                ans = dfs(pos, i - 1, 6 - pos[i] - tgr[i]) + dfs(tgr, i - 1, 6 - pos[i] - tgr[i]) + 1;
                break;
            }
        }
        printf("Case %d: %lld\n", ++idc, ans);
    }
    return 0;
}
View Code

 

UVA1326 Jurassic Remains  双向搜索模板+二进制枚举      代码

题意:相当于给n个01串,选尽量多的串使他们的抑或和为0;n <= 25;

题解:双向搜索,先搜出前面抑或得到的子串,在搜后面得到的子串,比较相同;

#include<bits/stdc++.h>
using namespace std;
const int M = 30;
int a[M];
map <int, int> table;
inline int bitcount(int x){ 
    if(!x) return 0; 
    return bitcount(x >> 1) + (x&1);
}
char c[1005];
int main(){
    int n;
    while(scanf("%d", &n) == 1){
        
        for(int i = 1; i <= n; i++){
            scanf("%s", c);
            a[i] = 0; 
            int len = strlen(c);
            for(int j = 0; j < len; j++) a[i] ^= ( 1 << (c[j] - 'A') );
        }
        
        table.clear();
        int ans = 0;
        int n1 = n/2, n2 = n - n1;
        for(int s = 0; s < (1 << n1); s++){
            int x = 0;
            for(int i = 1; i <= n; i++)
                if((1 << (i-1)) & s) x ^= a[i];
            if(!table.count(x) || bitcount(s) > bitcount(table[x])) table[x] = s;
        }
        for(int s = 0; s < (1 << n2); s++){
            int x = 0;
            for(int i = n1 + 1; i <= n; i++)
                if((1 << (i-n1-1)) & s) x ^= a[i];
            if(table.count(x) && bitcount(s) + bitcount(table[x]) > bitcount(ans)){
                ans = table[x] + (s << n1);
            //    printf("%d %d %d %d\n", x, table[x], s, ans);
            } 
        }
        printf("%d\n", bitcount(ans));    
        if(ans){
            for(int i = 1; i <= n; i++)
                if((1 << (i - 1)) & ans)printf("%d ", i);    
        }
        puts("");
    }
    
}
View Code

 

UVA1467 Installations 贪心推理题                                         思维

题意,n(<=500)个工作,有持续时间和限制时间,每个工作惩罚时间= max(0, now - limit),安排工作顺序,使最大的两个惩罚值之和最小,求最小值;

题解:先按照结束时间为第一关键字,持续时间为第二关键字排序,这样可以保证最大的惩罚值最小,在此基础上第二大的最小;但是有可以最大的变大,第二大变小更优;

找到两个最大值的位置,p1, p2(p1 < p2);枚举p2之前的每个位置,把他放在p2后面,更新最大,第二大值,证明见https://blog.csdn.net/corncsd/article/details/18948953

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <ctime>
using namespace std;
const int M = 505;
int ta, tb, n, mx1, mx2, p;
struct Job{
    int s, t;
}a[M];
bool cmp(Job A, Job B){
    return A.t == B.t ? A.s < B.s : A.t < B.t;
}
int wrk(int x){
    ta = tb = 0;
    int now = 0;
    for(int i = 1; i <= p; i++){
        if(i == x)continue;
        now += a[i].s;
        int tp = max(0, now - a[i].t);
        if(tp > ta)tb = ta, ta = tp, mx2 = mx1, mx1 = i;
        else if(tp > tb)tb = tp, mx2 = i;
    }
    now += a[x].s;
    int tp = max(0, now - a[x].t);
    if(tp > ta)tb = ta, ta = tp, mx1 = x;
    else if(tp > tb)tb = tp, mx2 = x;
    return ta + tb;    
}

int main(){
    
    int T;
    scanf("%d", &T);
    while(T--){
        
        scanf("%d", &n);
        p = n;
        for(int i = 1; i <= n; i++)scanf("%d%d", &a[i].s, &a[i].t);
        sort(a + 1, a + 1 + n, cmp);
        a[0].t = 1e9;
        int ans = wrk(0);
        p = max(mx1, mx2);
        for(int i = 1; i <= p; i++)
            ans = min(ans, wrk(i));
        printf("%d\n", ans);    
    }    
}
View Code

 

 

UVA1335 Beijing Guards     贪心推理                                    思维

题意:n(<=100000)个人站成一圈,每个人要a个礼物,相邻两人礼物编号不可重复,每个编号有礼物无限个,求最小编号可以是多少;

题解:若n为偶数,答案是max(a[i]  + a[i+1]),规律好找;若n为奇数,我们就尽量让为偶数的人选编号小的礼物,奇数人选编号大的,这样可以避免冲突;编号就二分好了;

#include<bits/stdc++.h>
using namespace std;
const int M = 100000 + 5;
int n, lft[M], rigt[M], a[M];


bool check(int k){
    lft[1] = a[1]; rigt[1] = 0;
    int y = k - a[1];
    for(int i = 2; i <= n; i++){
        if(i%2){
            rigt[i] = min(a[i], y - rigt[i - 1]);
            lft[i] = a[i] - rigt[i];
        }
        else{
            lft[i] = min(a[i], a[1] - lft[i - 1]);
            rigt[i] = a[i] - lft[i];
        }
    }
    return lft[n] == 0;
}


int main(){
    //    freopen("c.in","r",stdin);
    //  freopen("my.out","w",stdout);
    
    //cout<<bitcount(6)<<endl;
    while(scanf("%d", &n) == 1 &&n){
        int ans = 0, L = 0, R = 0;
        for(int i = 1; i <= n; i++)scanf("%d", &a[i]);
        a[n + 1] = a[1];
        for(int i = 1; i <= n; i++)
            L = max(L, a[i] + a[i + 1]), R = max(R, a[i] * 3);
        if(n == 1)ans = a[1];   
        else if(n%2){
            int lf = L, rg = R;
            while(lf <= rg){
                int mid = (lf + rg) >> 1;
                if(check(mid))rg = mid - 1, ans = mid;
                else lf = mid + 1;
            }
        }
        else ans = L;
        printf("%d\n", ans);
    }
}
View Code

我在n=1是直接return a[1], 搞了半天,多组数据呵呵

 

UVA1099 Sharing Chocolate   好题                              思维+代码

题意:x*y大小的巧克力,问最后能否切成x1*y1,x2*y2,x3*y3……xn*yn (n <= 15)的巧克力;

题解:虽然n很小,但是想搜索很难啊;状压DP, dp[s][x][y] 表示选择集合为s的块,能否凑出x*y;然后可以把max(x, y)压掉,用总面积表示,细节看代码;

 

#include<bits/stdc++.h>
using namespace std;
const int M = (1 << 15) + 1;
int dp[M][105], sum[M], a[16];
inline int bitcount(int x){return x == 0 ? 0 : bitcount(x >> 1) + (x & 1);}
bool dfs(int r, int res){
    if(dp[res][r] != -1) return dp[res][r];
    if(bitcount(res) == 1) return dp[res][r] = 1;
    int y = sum[res]/r;
    for(int s0 = res&(res-1); s0; s0 = res&(s0-1)){
        int s1 = res - s0;
        if((sum[s0]%r == 0 && dfs(min(r, sum[s0]/r), s0)) && dfs(min(r, sum[s1]/r), s1))
        return dp[res][r] = 1;
        if((sum[s0]%y == 0 && dfs(min(y, sum[s0]/y), s0)) && dfs(min(y, sum[s1]/y), s1))
        return dp[res][r] = 1;
    }
    return dp[res][r] = 0;
}


int main(){
    int n, X, Y, idc = 0;
    //cout<<bitcount(6)<<endl;
    while(scanf("%d", &n)&&n){
        memset(sum, 0, sizeof(sum));
        memset(dp, -1, sizeof(dp));
        scanf("%d%d", &X, &Y);
        for(int i = 1; i <= n; i++)scanf("%d", &a[i]);
        for(int s = 1; s < (1 << n); s++)
            for(int i = 1; i <= n; i++)
                if(s & (1 << (i - 1))) sum[s] += a[i];
        int All = (1 << n) - 1;
        if(sum[All] % X || sum[All] != X*Y){
            printf("Case %d: No\n", ++idc);continue;
        }
        bool ans = dfs(min(X, Y), All);
        printf("Case %d: ",++idc);
        ans == 1 ? puts("Yes") : puts("No");
    }
}
View Code

 

UVA11090 Going in Cycle!!   SPFA跑负环                            思维

给定一个N个点M条边的带权有向图,求平均值最小的回路

N<=50

题解:我们要求平均权值最小,所以考虑二分答案。

当它小于mid的时候,这就意味着: a_1+a_2+......+a_k<k*mid
即:(a_1-mid)+(a_2-mid)+......(a_k-mid)<0
这时我们发现只要我们开始的时候将各个边处理一下,之后需要的操作就是判负环了.......

(我写二分时,用ans记录合法的L,R,但有一组因为精度原因,一来就L==R,而ans初值为1,我因为这个原因wa了一页)

# include <bits/stdc++.h>

const  double  eps = 1e-8 , dnf = 5e13 + 7 ;
const  int  N = 10000 + 5 ;

int  head [ N ] , nxt [ N << 1 ] , to [ N << 1 ] , cn ;
int  num [ N ] , x , y ;
double  dis [ N ] , w [ N ] , z ;
int  n , m , T ;
bool  vis [ N ] ;

std :: queue < int >  q ;

void  create ( int  u , int  v , double  d ) {
    cn ++ ;
    w [ cn ] = d ;
    to [ cn ] = v ;
    nxt [ cn ] = head [ u ] ;
    head [ u ] = cn ;
}

bool  spfa ( int  s , double  l ) {
    memset ( vis , false , sizeof ( vis ) ) ;
    for ( int  i = 1 ; i <= n ; i ++ )
        dis [ i ] = dnf ;
    memset ( num , 0 , sizeof ( num ) ) ;
    q . push ( s ) ;
    dis [ s ] = 0 ;
    while ( ! q . empty ( ) ) {
        int  u = q . front ( ) ; q . pop ( ) ; vis [ u ] = false ;
        for ( int  i = head [ u ] ; i ; i = nxt [ i ] ) {
            int  v = to [ i ] ;
            if ( dis [ v ] > dis [ u ] + w [ i ] - l ) {
                dis [ v ] = dis [ u ] + w [ i ] - l ;
                if ( ! vis [ v ] ) {
                    num [ v ] ++ ;
                    if ( num [ v ] > n )  return  true ;
                    vis [ v ] = true ;
                    q . push ( v ) ;
                }
            }
        }
    }
    return  false ;
}

bool  check ( double  lim ) {
    for ( int  i = 1 ; i <= n ; i ++ )
        if ( spfa ( i , lim ) )
            return  true ;
    return  false ;
}

double  divx ( double  l , double  r ) {
    double  ans = - 1 ;
    while ( l <= r - eps ) {
        double  mid = ( r + l ) / 2 ;
        if ( check ( mid ) )
            r = mid - eps , ans = mid ;
        else 
            l = mid + eps;
    }
    return  ans ;
}

void  init ( ) {
    cn = 0 ;
    memset ( head , 0 , sizeof ( head ) ) ;
}

/*
1
1 1
1 1 3.5
*/

int  main ( ) {
    
    int  kase = 0 ;
    
    scanf ( "%d" , & T ) ;
    
    while ( T -- ) {
        
        kase ++ ;
        
        init ( ) ;
        
        scanf ( "%d%d" , & n , & m ) ;
        
        for ( int  i = 1 ; i <= m ; i ++ )
            scanf ( "%d%d%lf" , & x , & y , & z ) , create ( x , y , z ) ;
        
        double  ansx = divx ( 0 , 1e8 ) ;
        
        if ( ansx == - 1 )  printf ( "Case #%d: No cycle found.\n" , kase ) ;
        else  printf ( "Case #%d: %.2lf\n" , kase , ansx ) ;
        
    }
    
    return  0 ;
}
View Code

 

UVA11374 Airport Express     dijstra                           思维+代码

题意:

IokhIokh市中,机场快线是市民从市内去机场的首选交通工具。机场快线分为经济线和商业线两种,线路、速度和价钱都不同。你有一张商业线车票,可以坐一站商业线,而其他时候只能乘坐经济线。假设换乘时间忽略不计,你的任务是找一条去机场最快的线路。
输入格式:
输入包含多组数据。每组数据第一行为33个整数N, ,S和E(2N500,1S,E100),即机场快线中的车站总数,起点和终点(即机场所在站)编号。下一行包含一个整数M(1M1000),即经济线的路段条数。以下M行每行3个整数X, Y, Z,K(1K1000),以下KK行是这些路段的描述,格式同经济线。所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
输出格式:
对于每组数据,输出3行。第一行按访问顺序给出经过的各个车站(包括起点和终点),第二行是换乘商业线的车站编号(如果没有商业线车票,输出Ticket Not Used),第三行是总时间。

题解:

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int N = 505;
const int M = 5005;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n, s, t;
int vis[N], d[2][N], en;
int head[M];
int pre[2][N];

struct node {  
    int to, dis, next;  
}edge[N * M];  

void init();

void addEdge(int u,int v,int x) {  
    edge[en].to = v;  
    edge[en].next = head[u];  
    edge[en].dis = x;  
    head[u] = en++;  

    edge[en].to = u;  
    edge[en].next = head[v];  
    edge[en].dis = x;  
    head[v] = en++;  
}  

void SPFA(int flag) {  
    queue<int> Q;  
    for(int i = 1; i <= n; i++) {  
        d[flag][i] = INF;  
        vis[i] = 0;  
        pre[flag][i] = -1;  
    }  
    d[flag][s] = 0;  
    vis[s] = 1;  
    pre[flag][s] = s;  
    Q.push(s);  
    while(!Q.empty()) {  
        int u = Q.front();  
        vis[u] = 0;  
        Q.pop();  
        for(int i = head[u]; i != -1; i = edge[i].next) {  
            int v = edge[i].to;  
            //.fprintf(stderr, "%d ", v);
            if(d[flag][u] + edge[i].dis < d[flag][v]) {  
                d[flag][v] = d[flag][u] + edge[i].dis;  
                pre[flag][v] = u;  
                if(!vis[v]) {  
                    Q.push(v);  
                    vis[v] = 1;  
                }  
            }  
        }  
    }  
} 

void input() {
    int num, a, b, c;   
    scanf("%d", &num);
    for (int i = 0; i < num; i++) {
        scanf("%d %d %d", &a, &b, &c);  
        addEdge(a, b, c);
    }
}

void print(int x) {
    if (pre[0][x] == x) {
        printf("%d", x);    
        return;
    }
    print(pre[0][x]);
    printf(" %d", x);
}

void solve() {
    int num, a, b, c;
    int Min = d[0][s], tU = -1, tV = -1;
    scanf("%d", &num);
    for (int i = 0; i < num; i++) {
        scanf("%d %d %d", &a, &b, &c);  
        if (d[0][a] + d[1][b] + c < Min) {
            Min = d[0][a] + d[1][b] + c;
            tU = a, tV = b;
        }
        if (d[0][b] + d[1][a] + c < Min) {
            Min = d[0][b] + d[1][a] + c;
            tU = b, tV = a;
        }
    }
    if (tU == -1) {
        print(s);
        puts("");   
        printf("Ticket Not Used\n");
    } else {
        print(tU);
        for (int i = tV; i != s; i = pre[1][i]) printf(" %d", i);
        printf(" %d\n", s);
        printf("%d\n", tU);
    }
    printf("%d\n", Min);
}

int main() {
//    freopen("c.in","r",stdin);
//    freopen("std.out","w",stdout);
    int Case = 1;
    while (scanf("%d %d %d", &n, &s ,&t) == 3) {
        memset(head, -1, sizeof(head));en = 0;
        if (Case != 1) puts("");
        Case++;
        input();            
        SPFA(0);
        s = t;
        SPFA(1);
        solve();    
    }   
    return 0;
}
View Code

 

UVA10825 Anagram and Multiplication                               思维

题目大意:给出m和n,要求你求出一个m位的n进制数,这个数分别乘上1,2,..,m之后的值是这个数每位上的数的排列组合中的其中一种,求这个数

解题思路:这题的难点是怎么枚举这个数。因为要分别乘上1,2,..,m,如果我们枚举这个数的最后一位的话,乘上这些数后不然得出最后一位的值,而这些值必然是属于这个数的,只是不知道再那个位置上而已,所以要对这些数进行排列组合,然后再进行确认,确认的过程就是暴力的过程

#include<bits/stdc++.h>
using namespace std;
int n, m;

int arr[10];
bool vis[10];
#define RG register

int main(){
    //freopen("c.in","r",stdin);
    //freopen("c.out","w",stdout);
    while(scanf("%d%d", &m, &n) == 2){
        if(!n&&!m)break;
        int ok=0;
        for(int i = 1; i < n; i++){
            arr[1] = i;
            for(int j = 2; j <= m; j++) arr[j] = i * j % n;
            //for(int j = 1; j <= m; j++) printf("%d", arr[j]);puts("");
            sort(arr + 1, arr + 1 + m);
            
            do{
                int fg = 0;
                
                for(RG int p = 2; p <= m; p++){
                    memset(vis, 0, sizeof(vis));
                    int tot = 0, c = 0;
                    fg = 0;
                    for(RG int j = 1; j <= m; j++){
                        int now = (arr[j] * p + c) % n;
                        c = (arr[j] * p + c) / n;
                        //if(check())printf("%d %d %d   ", arr[j], now, c);
                        for(RG int k = 1; k <= m; k++)
                            if(!vis[k] && now == arr[k]){vis[k] = 1; tot++; break;}
                    }
                    /*if(check()){
                        printf("\n%d: ", p);
                        for(int j=1;j<=m;j++) if(vis[j])printf("%d ", arr[j]);
                        printf("FUCK THAT%d\n", tot);
                    }*/
                    if(tot != m || !arr[m]){fg = 1; break;}
                }
                
                if(!fg){
                    for(int j = m; j>1; j--){
                        printf("%d ", arr[j]);
                    }
                    printf("%d\n",arr[1]);
                    ok = 1;
                    break;
                }
                
            }while(next_permutation(arr + 1, arr + 1 + m));
            if(ok)break;
        }
        if(!ok)printf("Not found.\n");
    }
}
View Code

 

UVA11538 Chess Queen                                                数学+思维

题目大意:给定一个棋盘,在棋盘上放两个皇后(一白一黑),求使得两个皇后相互攻击(在一行、一列或对角线)的方案数。

解题思路:

1、我们首先可以想到的是一个皇后在一个任意的位置,另一个皇后可以放的在同一行或在同一列的方案数有(m-1+n-1),棋盘上一共有m*n个格子,所以可放在同行或同列相互攻击的方案是ans=(m-1+n-1)*m*n;

2、同行同列的方案数已经算出来了,现在我们计算对角线上的方案数吧,因为左斜和右斜的方案是一样多的,所以我们只要求出左斜线即可知道右斜线的方案数。

3、左斜线,首先我们要考虑到的是:斜线的最大值,即为min(m,n),那我们就把x=max(m,n)看作行,y=min(m,n)看作列,所以我们的2~y-1行每行的格子数就是行号数,大于等于y的行号数都只有y个格子,所以等于Y个格子的左斜线有(y-x+1)——每一条上可以任选两个点放置皇后,即cnt+=i*(i-1)*2; cnt+=(n-m+1)*m*(m-1);

4、即总方案数:ans+2*cnt;

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll unsigned long long

int main(){
    ll n, m; 
    while(cin>>n>>m ){
        if(!n&&!m)break;
        if(n > m) swap(n, m);
        ll ans = m*(m-1)*n + n*(n-1)*m;
        ans -= n*(n-1)*2;
        ans += 2*(n-1)*n*(2*n - 1)/3;
        ans += (m-n+1)*(n-1)*n*2;
        //printf("Case %d: %lld\n", ++idc, ans);
        cout<<ans<<endl;
    }
} 
View Code

 

 

 UVA11806 Cheerleaders  容斥原理+二进制            数学+思维+代码

题意:在一个m*n的矩形网格里放k个相同的石子,问有多少种方法?每个格子最多放一个石子,所有石子都要放完,并且第一行、最后一行、第一列、最后一列都得有石子。

思路:假设满足第一行没有石子的方案集为A,最后一行没有石子的方案集为B,第一列没有石子的方案集为C,最后一列没有石子的方案集为D,全集为S,则所求答案就是“在S中但不在A,B,C,D任何一个集合中”的元素个数,这里就是运用容斥原理。程序中可以用二进制来表示集合。

#include <cstdio>
#include <cstdlib>
#include <ctime>
int C[505][505];
const int mod = 1000007;
inline int moc(int a){return a >= mod ? a - mod : a;}
void init(){
    for(int i = 0; i <= 500; i++)
        for(int j = 0; j <= i; j++)
            if(i == j || j == 0) C[i][j] = 1;
            else C[i][j] = moc(C[i - 1][j] + C[i - 1][j - 1]);
}


int main() {
    init();
    int T;
    scanf("%d", &T);
    for(int j = 1; j <= T; j++){
        int n, m, k, sum = 0;
        int ans = 0;
        scanf("%d%d%d", &n, &m, &k);
        for(int s = 0; s < 16; s++){
            int b = 0, r = n, c = m;
            if(s & 1) {r--, b++;}
            if(s & 2) {r--, b++;}
            if(s & 4) {c--, b++;}
            if(s & 8) {c--, b++;}
            if(b&1) ans = (mod - C[r*c][k] + ans) % mod;
            else ans = moc(ans + C[r*c][k]);
        }
        printf("Case %d: %d\n", j, ans);
    }
}
View Code

 

 UVA1374 Power Calculus 迭代搜索

 

两个剪枝:如果以指数增长不可以满足,就byebye;

数不会超过2*n;

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define ll long long
const int M = 2e5 + 10;
int n; 
int a[2005] = {0, 1}, fg;
void dfs(int up, int dep){
    if(a[dep] == n){
        fg = dep-1; return ;
    }
    if(a[dep] <<(up-dep+1) < n) return ;
    if(dep > up) return;
    for(int i = 1; i <= dep; i++){
        if(fg) return ;
        int t = a[dep] + a[i];
        if(t > 0 && t <= 2*n){
            a[dep+1] = t;
            dfs(up, dep+1);
        }
        t = a[dep] - a[i];
        if(t > 0 && t <= 2*n){
            a[dep+1] = t;
            dfs(up, dep+1);
        }
    }
    
}


int main(){
    while(scanf("%d", &n)&&n){
        fg = 0; int i = 1;
        if(n == 1) {printf("0\n");continue;}
        while(1){
            dfs(i, 1); 
            if(fg)break;
            i++;
        }
        printf("%d\n",i);
    }
    
    
}
View Code

 

UVA1394 And Then There Was One 经典的猴子报数     思维

题意:最初,n个石头按照顺时针的顺序围成一个圈,它们的编号是1,...,n.现在你将会得到两个数字k和m。之后,移除石子m,然后数k个石子移除一个,重复这个操作直到只剩下一个石子为止,求这个最后剩下的石子的编号。(与约瑟夫问题类似)

题解:

 

UVA1481 Genome Evolution 计算几何                                思维+代码

题目大意:给你n个点和一个m×m的坐标纸(坐标范围0-m-1),n个点中前两个为A和B,要求给出坐标纸上满足以下性质的点的数目——某一个点满足条件当且仅当对任意一个给定点C,它对A曼哈顿距离小于C对A曼哈顿距离 或 它对B曼哈顿距离小于C对B曼哈顿距离。

思路:显然符合条件的点都在AB之间(因为如果在AB两侧则假设此点在A左侧,则此点到A距离小于A,到B距离也小于B,不符合题意)。则对于任意一个题目输入的点,它的可行域(即对于此点符合条件的点的集合)如下:

则整个题目给定点的可行域为所有单个点可行域的交集,则大致如下图:

#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 5,  INF = 2e9;
int b[M], a[M], pos[M];
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int up[M];
inline int ab(int a){return a>=0?a:-a;}

int main(){
    int n;
    while(scanf("%d", &n)&&n){
        for(int i = 1; i <= n; i++) a[i] = read();
        for(int i = 1; i <= n; i++) b[i] = read(), pos[b[i]] = i;
        int cnt = 0;
        for(int st = 1; st < n; st++){
            int lf = pos[a[st]], rg = pos[a[st]], L = lf, R = rg;
            for(int ed = st + 1; ed <= n; ed++){
                int x = pos[a[ed]];
                if(x < lf) lf = x;
                if(x > rg) rg = x;
                if(L > lf) L--;
                else R++;
                if(L == lf && rg == R) cnt++;
            }
            
        }
        printf("%d\n",cnt);
    }
    
}
View Code

 

 

uva 11400 lighting system design  贪心+DP              思维

题目大意:给出n个模式,每个模式有电压v,电压费用k,每盏灯的花费c以及灯数l。然后电压高的可以用于电压低得。问说最少花费多少钱可以满足n个模式。

题解:首先贪心,对于每种灯泡要不然全换,要不然全不换,费用节省主要是由于替换灯泡类型而排除了低压电源,将灯泡按电压上升排序,DPi表示考虑了前i个灯泡的最优解,我就只需要枚举之前是哪换的,DPj是有许多种灯泡组成的,所以用它更新考虑了替换不同电压灯泡的情况,相当于灯泡先换成j,再换为i.

 

uva Fewest Flops        贪心+DP状态定义               思维

题意:输入一个正整数 k 和一个字符串 S, 字符串的长度保证为 k 的整数倍。把 S 的字符串从左至右的顺序每 k 个分成一组,每组里面的字符串可以任意重排,但组与组间的顺序不能改变。你的任务是使重排后的字符串包含的 “块”

尽量的少,每个 “块” 为连续的相同的字母。比如 uuvuwwuv 可以分为两组:uuvu 和 wwuv,第一组重排为 uuuv , 第二组重排为 vuww,连接起来就是 uuuvvuww, 包含4个 “块”。

思路:

dp[i][j]: 第 i 块以第 j 位结尾时的最小块数

对于每个单独的一块,它的chunks就是等于出现的不同字母数

第 i 块的chunks记做 chunks[i]

如果第 i-1 块的最后一位和第 i 块的第一位是一样的,那么可以合并这两个,总chunks可以减少一个

dp[i][j] = min{  如果i-1块的第k位和i块的第一位不同:dp[i-1][k]+chunks[i], 

                       如果i-1块的第k位和i块的第一位相同: dp[i-1][k]+chunks[i]-1  }

#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
 
const int INF = 1<<29;
const double PI = acos(-1.0);
const double e = 2.718281828459;
const double eps = 1e-8;
const int MAXN = 1010;
int vis[256];
char s[MAXN];
int dp[MAXN][MAXN];
 
int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int Case, kk, chunk;
    cin>>Case;
    while(Case--)
    {
        cin>>kk>>s;
        int len = strlen(s);
        for(int i = 0; i < len/kk; i++)
        {
            fill(dp[i], dp[i]+kk, INF);
            memset(vis, 0, sizeof(vis));
            chunk = 0;
            for(int j = i*kk; j <= (i+1)*kk-1; j++)
            {
                if(!vis[s[j]])
                {
                    vis[s[j]] = 1;
                    chunk++;
                }
            }
            if(!i)
            {
                for(int j = 0; j < kk; j++)
                    dp[i][j] = chunk;
                continue;
            }
            for(int j = 0; j < kk; j++)
            {
                for(int k = 0; k < kk; k++)
                {   // 在第 i-1 块里面选择第 k 位的字母,作为第 i-1 块的末位,同时作为第 i 块的首位
                    int front = (i-1)*kk+k;
                    int rear = i*kk+j;// 在第 i 块里面选择第 j 位的字母,作为第 i 块的末位
                    if(vis[s[front]] && (chunk==1||s[front]!=s[rear])) 
                        //第 i 块的首位和末位不可能为同个字母,除非第 i 块的所有字母都相同
                        dp[i][j] = min(dp[i][j], dp[i-1][k]+chunk-1);
                    else
                        dp[i][j] = min(dp[i][j], dp[i-1][k]+chunk);
                }
            }
        }
        int ans = INF;
        for(int i = 0; i < kk; i++)
            ans = min(ans, dp[len/kk-1][i]);
        cout<<ans<<endl;
    }
    return 0;
}
View Code

 

UVA1169 Robotruck   线段树、单调队列优化DP

题目描述】

有n个垃圾,第i个垃圾的坐标为(xi,yi),重量为wi。有一个机器人,要按照编号从小到大的顺序捡起所有垃圾并扔进垃圾桶(垃圾桶在原点(0,0))。机器人可以捡起几个垃圾以后一起扔掉,但任何时候其手中的垃圾总重量不能超过最大载重C。两点间的行走距离为曼哈顿距离(即横坐标之差的绝对值加上纵坐标之差的绝对值)。求出机器人行走的最短总路程(一开始,机器人在(0,0)处)。

【输入格式】

输入的第一行为数据组数。每组数据的第一行为最大承重C(1<=C<=100);第二行为正整数n(1<=n<=100000),即垃圾的数量;以下n行每行为两个非负整数x,y和一个正整数w,即坐标和重量(重量保证不超过C)。

【输出格式】

对于每组数据,输出总路径的最短长度。

这个可以线段树优化;

 

#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 5, INF = 2e9;
int dis[M], sum[M], x[M], y[M], ls[M], c[M], dp[M];
inline int ab(int a){return a >= 0 ? a : -a;}
int n, C;
struct Node{
    int v; 
    Node *ls, *rs;
    void up(){
        v = min(ls->v, rs->v);
    }
}pool[M << 2], *root, *tail = pool;
#define Ls nd->ls, lf, mid
#define Rs nd->rs, mid+1, rg 
Node *build(int lf = 0, int rg = n){
    Node *nd = ++tail;
    if(lf == rg)nd->v = INF;
    else {
        int mid = (lf + rg) >> 1;
        nd->ls = build(lf, mid);
        nd->rs = build(mid+1, rg);
        nd->up();
    }
    return nd;
}
void add(int pos, int d, Node *nd = root, int lf = 0, int rg = n){
    if(lf == rg)nd->v = d;
    else {
        int mid = (lf + rg) >> 1;
        if(pos <= mid) add(pos, d, Ls);
        else add(pos, d, Rs);
        nd->up();
    }
    return ;
}
int query(int L, int R, Node *nd = root, int lf = 0, int rg = n){
    if(L <= lf && rg <= R) return  nd->v;
    int mid = (lf + rg) >> 1;
    int ans = INF;
    if(L <= mid) ans = query(L, R, Ls);
    if(R > mid) ans = min(ans, query(L, R, Rs));
    return ans;
}
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}


int main(){
    int T = read();
    while(T--){
    
        C = read();
        n = read();
        tail = pool;
        root = build();    
        
        int lst = 0;
        for(int i = 1; i <= n; i++){
            x[i] = read(), y[i] = read(), c[i] = read();
            sum[i] = sum[i - 1] + c[i];
            while(sum[i] - sum[lst] > C) lst++;
            ls[i] = lst;
            dis[i] = dis[i - 1] + ab(x[i] - x[i-1]) + ab(y[i] - y[i-1]);
        }
        dp[0] = 0;
        add(0, 0);
        for(int i = 1; i <= n; i++){
            dp[i] = query(ls[i], i - 1) + x[i] + y[i] + dis[i];
            add(i, dp[i] - dis[i+1] + x[i+1] + y[i+1]);
        }
        printf("%d\n", dp[n]);
        if(T > 0) printf("\n");
        
    }
}
View Code

 

 

UVA10891 Game of Sum   DP重复状态优化    

题目描述

有一个长度为n的整数序列,两个游戏者A和B轮流取数,A先取。每次玩家只能从左端或者右端取任意数量的数,但不能两边都取。所有数都被取走视为游戏结束,然后统计每个人取走的数之和,作为各自的得分。两个人采取的策略都是让自己得分尽可能高,并且两个人都很机智,求A得分-B得分后的结果。 输入格式

输入包含多组数据,每组数据第一行为正整数n(1<=n<=100) ,第二行为给定的整数序列,输入结束标志是n=0 输出格式

对于每组数据,输出A和B都采取最优策略下,A的得分-B的得分

#include<bits/stdc++.h>
using namespace std;
const int M = 105,  INF = 2e9;
int sum[M], a[M];
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int dp[M][M], g[M][M], f[M][M];

int main(){
//    freopen("c.in","r",stdin);
//    freopen("c.out","w",stdout);
    int n, cas = 0;
    while(scanf("%d",&n) && n){
        for(int i = 1; i <= n; i++) {
            a[i] = read(), sum[i] = sum[i - 1] + a[i];
            dp[i][i] = f[i][i] = g[i][i] = a[i];
        }
            
        for(int l = 2; l <= n; l++){
            for(int i = 1; i + l - 1 <= n; i++){
                int j = i + l - 1;
                int m = 0;
                m = min(m, min(f[i+1][j], g[i][j-1]));
                dp[i][j] = sum[j] - sum[i-1] - m;
                f[i][j] = min(f[i+1][j], dp[i][j]);
                g[i][j] = min(g[i][j-1], dp[i][j]);
                //printf("%d %d %d\n",i,j,dp[i][j]);
            }
        }
        printf("%d\n", 2*dp[1][n] - sum[n]);
    }
}
View Code

 

 

 

UVA11825 Hackers' Crackdown      数学模型        代码+思维

题目描述

假如你是一个黑客,侵入了一个有着n台计算机(编号为1.2.3....n)的网络。一共有n种服务,每台计算机都运行着所有服务。对于每台计算机,你都可以选择一项服务,终止这台计算机和所有与它相邻计算机的该项服务(如果其中一些服务已经停止,那他们继续保持停止状态)。你的目标是让尽量多的服务完全瘫痪(即:没有任何计算及运行着该服务) 输入格式

输入包含多组数据,每组数据的第一行为整数n(1<=n<=16):以下n行每行描述一台计算机相邻的计算机,其中第一个数m为相邻计算机个数,接下来的m个整数为这些计算机的编号。输入结束标志n=0. 输出格式

对于每组数据,输出完全瘫痪的服务的数量

#include<bits/stdc++.h>
using namespace std;
const int M = (1<<16) + 1,  INF = 2e9;

int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int f[M], cover[M], P[M];

int main(){
    int n, cas = 0;
    while(scanf("%d",&n) && n){
        memset(P, 0, sizeof(P));
        memset(cover, 0, sizeof(cover));
        memset(f, 0x8f, sizeof(f));
        ++cas;
        for(int i = 0; i < n; i++){
            int m = read();
            P[i] |= (1<<i);
            while(m--){
                int x = read();
                P[i] |= (1<<x);
            }
        }
        int all = (1<<n) - 1;
        for(int s = 0; s <= all; s++)
            for(int i = 0; i < n; i++)
                if(s&(1<<i)) cover[s] |= P[i];
        f[0] = 0;
        for(int S = 0; S <= all; S++)
            for(int S0 = S; S0; S0 = S&(S0-1)){
                if(cover[S0] == all) f[S] = max(f[S], f[S^S0] + 1);
            }
        printf("Case %d: %d\n", cas, f[all]);
    }
}
View Code

 

posted @ 2018-10-23 18:22  Ed_Sheeran  阅读(38)  评论(0)    收藏  举报